_ZN3ada4idna13utf8_to_utf32EPKcmPDi:
  160|  13.5k|size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) {
  161|  13.5k|  const uint8_t* data = reinterpret_cast<const uint8_t*>(buf);
  162|  13.5k|  size_t pos = 0;
  163|  13.5k|  const char32_t* start{utf32_output};
  164|   197k|  while (pos < len) {
  ------------------
  |  Branch (164:10): [True: 184k, False: 12.6k]
  ------------------
  165|       |    // try to convert the next block of 16 ASCII bytes
  166|   184k|    if (pos + 16 <= len) {  // if it is safe to read 16 more
  ------------------
  |  Branch (166:9): [True: 110k, False: 73.9k]
  ------------------
  167|       |                            // bytes, check that they are ascii
  168|   110k|      uint64_t v1;
  169|   110k|      std::memcpy(&v1, data + pos, sizeof(uint64_t));
  170|   110k|      uint64_t v2;
  171|   110k|      std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  172|   110k|      uint64_t v{v1 | v2};
  173|   110k|      if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (173:11): [True: 8.17k, False: 102k]
  ------------------
  174|  8.17k|        size_t final_pos = pos + 16;
  175|   138k|        while (pos < final_pos) {
  ------------------
  |  Branch (175:16): [True: 130k, False: 8.17k]
  ------------------
  176|   130k|          *utf32_output++ = char32_t(buf[pos]);
  177|   130k|          pos++;
  178|   130k|        }
  179|  8.17k|        continue;
  180|  8.17k|      }
  181|   110k|    }
  182|   176k|    uint8_t leading_byte = data[pos];  // leading byte
  183|   176k|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (183:9): [True: 99.6k, False: 76.7k]
  ------------------
  184|       |      // converting one ASCII byte !!!
  185|  99.6k|      *utf32_output++ = char32_t(leading_byte);
  186|  99.6k|      pos++;
  187|  99.6k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (187:16): [True: 18.2k, False: 58.4k]
  ------------------
  188|       |      // We have a two-byte UTF-8
  189|  18.2k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (189:11): [True: 159, False: 18.0k]
  ------------------
  190|    159|        return 0;
  191|    159|      }  // minimal bound checking
  192|  18.0k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (192:11): [True: 141, False: 17.9k]
  ------------------
  193|    141|        return 0;
  194|    141|      }
  195|       |      // range check
  196|  17.9k|      uint32_t code_point =
  197|  17.9k|          (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
  198|  17.9k|      if (code_point < 0x80 || 0x7ff < code_point) {
  ------------------
  |  Branch (198:11): [True: 15, False: 17.9k]
  |  Branch (198:32): [True: 0, False: 17.9k]
  ------------------
  199|     15|        return 0;
  200|     15|      }
  201|  17.9k|      *utf32_output++ = char32_t(code_point);
  202|  17.9k|      pos += 2;
  203|  58.4k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (203:16): [True: 56.5k, False: 1.93k]
  ------------------
  204|       |      // We have a three-byte UTF-8
  205|  56.5k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (205:11): [True: 42, False: 56.5k]
  ------------------
  206|     42|        return 0;
  207|     42|      }  // minimal bound checking
  208|       |
  209|  56.5k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (209:11): [True: 69, False: 56.4k]
  ------------------
  210|     69|        return 0;
  211|     69|      }
  212|  56.4k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (212:11): [True: 12, False: 56.4k]
  ------------------
  213|     12|        return 0;
  214|     12|      }
  215|       |      // range check
  216|  56.4k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
  217|  56.4k|                            (data[pos + 1] & 0b00111111) << 6 |
  218|  56.4k|                            (data[pos + 2] & 0b00111111);
  219|  56.4k|      if (code_point < 0x800 || 0xffff < code_point ||
  ------------------
  |  Branch (219:11): [True: 12, False: 56.4k]
  |  Branch (219:33): [True: 0, False: 56.4k]
  ------------------
  220|  56.4k|          (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (220:12): [True: 9.63k, False: 46.7k]
  |  Branch (220:35): [True: 9, False: 9.62k]
  ------------------
  221|     21|        return 0;
  222|     21|      }
  223|  56.4k|      *utf32_output++ = char32_t(code_point);
  224|  56.4k|      pos += 3;
  225|  56.4k|    } else if ((leading_byte & 0b11111000) == 0b11110000) {  // 0b11110000
  ------------------
  |  Branch (225:16): [True: 1.59k, False: 342]
  ------------------
  226|       |      // we have a 4-byte UTF-8 word.
  227|  1.59k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (227:11): [True: 33, False: 1.56k]
  ------------------
  228|     33|        return 0;
  229|     33|      }  // minimal bound checking
  230|  1.56k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (230:11): [True: 36, False: 1.52k]
  ------------------
  231|     36|        return 0;
  232|     36|      }
  233|  1.52k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (233:11): [True: 9, False: 1.51k]
  ------------------
  234|      9|        return 0;
  235|      9|      }
  236|  1.51k|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (236:11): [True: 15, False: 1.50k]
  ------------------
  237|     15|        return 0;
  238|     15|      }
  239|       |
  240|       |      // range check
  241|  1.50k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  242|  1.50k|                            (data[pos + 1] & 0b00111111) << 12 |
  243|  1.50k|                            (data[pos + 2] & 0b00111111) << 6 |
  244|  1.50k|                            (data[pos + 3] & 0b00111111);
  245|  1.50k|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (245:11): [True: 21, False: 1.47k]
  |  Branch (245:35): [True: 12, False: 1.46k]
  ------------------
  246|     33|        return 0;
  247|     33|      }
  248|  1.46k|      *utf32_output++ = char32_t(code_point);
  249|  1.46k|      pos += 4;
  250|  1.46k|    } else {
  251|    342|      return 0;
  252|    342|    }
  253|   176k|  }
  254|  12.6k|  return utf32_output - start;
  255|  13.5k|}
_ZN3ada4idna22utf32_length_from_utf8EPKcm:
  270|  13.6k|size_t utf32_length_from_utf8(const char* buf, size_t len) {
  271|  13.6k|  const int8_t* p = reinterpret_cast<const int8_t*>(buf);
  272|  13.6k|  return std::count_if(p, std::next(p, len), [](int8_t c) {
  273|       |    // -65 is 0b10111111, anything larger in two-complement's
  274|       |    // should start a new code point.
  275|  13.6k|    return c > -65;
  276|  13.6k|  });
  277|  13.6k|}
_ZN3ada4idna7deflate9BitReader3getEi:
  405|   457k|  uint32_t get(int n) {
  406|   457k|    if (!ensure(n)) return UINT32_MAX;
  ------------------
  |  Branch (406:9): [True: 0, False: 457k]
  ------------------
  407|   457k|    uint32_t v = acc & ((1u << n) - 1);
  408|   457k|    acc >>= n;
  409|   457k|    bits -= n;
  410|   457k|    return v;
  411|   457k|  }
_ZN3ada4idna9ascii_mapEPcm:
 5126|  6.09k|void ascii_map(char* input, size_t length) {
 5127|  6.09k|  auto broadcast = [](uint8_t v) -> uint64_t {
 5128|  6.09k|    return 0x101010101010101ull * v;
 5129|  6.09k|  };
 5130|  6.09k|  uint64_t broadcast_80 = broadcast(0x80);
 5131|  6.09k|  uint64_t broadcast_Ap = broadcast(128 - 'A');
 5132|  6.09k|  uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1);
 5133|  6.09k|  size_t i = 0;
 5134|       |
 5135|  54.4k|  for (; i + 7 < length; i += 8) {
  ------------------
  |  Branch (5135:10): [True: 48.3k, False: 6.09k]
  ------------------
 5136|  48.3k|    uint64_t word{};
 5137|  48.3k|    std::memcpy(&word, input + i, sizeof(word));
 5138|  48.3k|    word ^=
 5139|  48.3k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 5140|  48.3k|    std::memcpy(input + i, &word, sizeof(word));
 5141|  48.3k|  }
 5142|  6.09k|  if (i < length) {
  ------------------
  |  Branch (5142:7): [True: 5.29k, False: 800]
  ------------------
 5143|  5.29k|    uint64_t word{};
 5144|  5.29k|    std::memcpy(&word, input + i, length - i);
 5145|  5.29k|    word ^=
 5146|  5.29k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 5147|  5.29k|    std::memcpy(input + i, &word, length - i);
 5148|  5.29k|  }
 5149|  6.09k|}
_ZN3ada4idna3mapENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEERNS1_12basic_stringIDiS4_NS1_9allocatorIDiEEEE:
 5153|  20.7k|bool map(std::u32string_view input, std::u32string& out) {
 5154|       |  //  [Map](https://www.unicode.org/reports/tr46/#ProcessingStepMap).
 5155|  20.7k|  out.clear();
 5156|  20.7k|  if (!ensure_tables()) {
  ------------------
  |  Branch (5156:7): [True: 0, False: 20.7k]
  ------------------
 5157|      0|    return false;
 5158|      0|  }
 5159|       |
 5160|       |  // Pass 1: validate and compute exact output length.
 5161|  20.7k|  size_t out_size = 0;
 5162|   361k|  for (char32_t x : input) {
  ------------------
  |  Branch (5162:19): [True: 361k, False: 20.1k]
  ------------------
 5163|   361k|    uint16_t status = idna_lookup(static_cast<uint32_t>(x));
 5164|   361k|    if (status == IDNA_DISALLOWED) {
  ------------------
  |  Branch (5164:9): [True: 603, False: 360k]
  ------------------
 5165|    603|      return false;
 5166|    603|    }
 5167|   360k|    if (status == IDNA_VALID) {
  ------------------
  |  Branch (5167:9): [True: 253k, False: 107k]
  ------------------
 5168|   253k|      ++out_size;
 5169|   253k|      continue;
 5170|   253k|    }
 5171|   107k|    if (static_cast<size_t>(status) >= idna_utf8_mappings_size) {
  ------------------
  |  Branch (5171:9): [True: 0, False: 107k]
  ------------------
 5172|      0|      return false;
 5173|      0|    }
 5174|   107k|    out_size += utf8_count_codepoints(idna_utf8_mappings + status);
 5175|   107k|  }
 5176|       |
 5177|       |  // Pass 2: write into a single allocation.
 5178|  20.1k|  out.resize(out_size);
 5179|  20.1k|  size_t w = 0;
 5180|   359k|  for (char32_t x : input) {
  ------------------
  |  Branch (5180:19): [True: 359k, False: 20.1k]
  ------------------
 5181|   359k|    uint16_t status = idna_lookup(static_cast<uint32_t>(x));
 5182|   359k|    if (status == IDNA_VALID) {
  ------------------
  |  Branch (5182:9): [True: 252k, False: 106k]
  ------------------
 5183|   252k|      out[w++] = x;
 5184|   252k|      continue;
 5185|   252k|    }
 5186|       |    // IGNORED or mapped (status already validated in pass 1).
 5187|   106k|    const uint8_t* ptr = idna_utf8_mappings + status;
 5188|   621k|    while (*ptr != 0) {
  ------------------
  |  Branch (5188:12): [True: 514k, False: 106k]
  ------------------
 5189|   514k|      out[w++] = utf8_next(ptr);
 5190|   514k|    }
 5191|   106k|  }
 5192|  20.1k|  return true;
 5193|  20.7k|}
_ZN3ada4idna28compute_decomposition_lengthENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5286|  6.39k|    const std::u32string_view input) noexcept {
 5287|  6.39k|  bool decomposition_needed{false};
 5288|  6.39k|  size_t additional_elements{0};
 5289|  84.8k|  for (char32_t current_character : input) {
  ------------------
  |  Branch (5289:35): [True: 84.8k, False: 6.39k]
  ------------------
 5290|  84.8k|    size_t decomposition_length{0};
 5291|       |
 5292|  84.8k|    if (current_character >= hangul_sbase &&
  ------------------
  |  Branch (5292:9): [True: 9.12k, False: 75.7k]
  ------------------
 5293|  9.12k|        current_character < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5293:9): [True: 5.53k, False: 3.58k]
  ------------------
 5294|       |      // Full NFD-style Hangul decomp for the decompose() step.
 5295|  5.53k|      decomposition_length = 2;
 5296|  5.53k|      if ((current_character - hangul_sbase) % hangul_tcount) {
  ------------------
  |  Branch (5296:11): [True: 3.09k, False: 2.44k]
  ------------------
 5297|  3.09k|        decomposition_length = 3;
 5298|  3.09k|      }
 5299|  79.3k|    } else if (current_character < 0x110000) {
  ------------------
  |  Branch (5299:16): [True: 79.3k, False: 0]
  ------------------
 5300|  79.3k|      const uint8_t di = decomposition_index[current_character >> 8];
 5301|  79.3k|      const uint16_t* const decomposition =
 5302|  79.3k|          decomposition_block_row(di) + (current_character % 256);
 5303|  79.3k|      decomposition_length = (decomposition[1] >> 2) - (decomposition[0] >> 2);
 5304|  79.3k|      if ((decomposition_length > 0) && (decomposition[0] & 1)) {
  ------------------
  |  Branch (5304:11): [True: 9.48k, False: 69.8k]
  |  Branch (5304:41): [True: 0, False: 9.48k]
  ------------------
 5305|      0|        decomposition_length = 0;
 5306|      0|      }
 5307|  79.3k|    }
 5308|  84.8k|    if (decomposition_length != 0) {
  ------------------
  |  Branch (5308:9): [True: 15.0k, False: 69.8k]
  ------------------
 5309|  15.0k|      decomposition_needed = true;
 5310|  15.0k|      additional_elements += decomposition_length - 1;
 5311|  15.0k|    }
 5312|  84.8k|  }
 5313|  6.39k|  return {decomposition_needed, additional_elements};
 5314|  6.39k|}
_ZN3ada4idna9decomposeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEEm:
 5316|  5.16k|void decompose(std::u32string& input, size_t additional_elements) {
 5317|  5.16k|  input.resize(input.size() + additional_elements);
 5318|  5.16k|  for (size_t descending_idx = input.size(),
 5319|  5.16k|              input_count = descending_idx - additional_elements;
 5320|  59.7k|       input_count--;) {
  ------------------
  |  Branch (5320:8): [True: 54.6k, False: 5.16k]
  ------------------
 5321|  54.6k|    if (input[input_count] >= hangul_sbase &&
  ------------------
  |  Branch (5321:9): [True: 7.13k, False: 47.4k]
  ------------------
 5322|  7.13k|        input[input_count] < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5322:9): [True: 5.53k, False: 1.59k]
  ------------------
 5323|       |      // Hangul decomposition.
 5324|  5.53k|      char32_t s_index = input[input_count] - hangul_sbase;
 5325|  5.53k|      if (s_index % hangul_tcount != 0) {
  ------------------
  |  Branch (5325:11): [True: 3.09k, False: 2.44k]
  ------------------
 5326|  3.09k|        input[--descending_idx] = hangul_tbase + s_index % hangul_tcount;
 5327|  3.09k|      }
 5328|  5.53k|      input[--descending_idx] =
 5329|  5.53k|          hangul_vbase + (s_index % hangul_ncount) / hangul_tcount;
 5330|  5.53k|      input[--descending_idx] = hangul_lbase + s_index / hangul_ncount;
 5331|  49.0k|    } else if (input[input_count] < 0x110000) {
  ------------------
  |  Branch (5331:16): [True: 49.0k, False: 0]
  ------------------
 5332|  49.0k|      const uint16_t* decomposition =
 5333|  49.0k|          decomposition_block_row(
 5334|  49.0k|              decomposition_index[input[input_count] >> 8]) +
 5335|  49.0k|          (input[input_count] % 256);
 5336|  49.0k|      uint16_t decomposition_length =
 5337|  49.0k|          (decomposition[1] >> 2) - (decomposition[0] >> 2);
 5338|  49.0k|      if (decomposition_length > 0 && (decomposition[0] & 1)) {
  ------------------
  |  Branch (5338:11): [True: 9.48k, False: 39.5k]
  |  Branch (5338:39): [True: 0, False: 9.48k]
  ------------------
 5339|      0|        decomposition_length = 0;
 5340|      0|      }
 5341|  49.0k|      if (decomposition_length > 0) {
  ------------------
  |  Branch (5341:11): [True: 9.48k, False: 39.5k]
  ------------------
 5342|  9.48k|        const size_t base = static_cast<size_t>(decomposition[0] >> 2);
 5343|  9.48k|        if (base + decomposition_length > decomposition_data_size) {
  ------------------
  |  Branch (5343:13): [True: 0, False: 9.48k]
  ------------------
 5344|      0|          input[--descending_idx] = input[input_count];
 5345|  9.48k|        } else {
 5346|  30.2k|          while (decomposition_length-- > 0) {
  ------------------
  |  Branch (5346:18): [True: 20.7k, False: 9.48k]
  ------------------
 5347|  20.7k|            input[--descending_idx] =
 5348|  20.7k|                decomposition_data[base + decomposition_length];
 5349|  20.7k|          }
 5350|  9.48k|        }
 5351|  39.5k|      } else {
 5352|  39.5k|        input[--descending_idx] = input[input_count];
 5353|  39.5k|      }
 5354|  49.0k|    } else {
 5355|      0|      input[--descending_idx] = input[input_count];
 5356|      0|    }
 5357|  54.6k|  }
 5358|  5.16k|}
_ZN3ada4idna7get_cccEDi:
 5360|  1.87M|uint8_t get_ccc(char32_t c) noexcept {
 5361|  1.87M|  if (c >= 0x110000) {
  ------------------
  |  Branch (5361:7): [True: 0, False: 1.87M]
  ------------------
 5362|      0|    return 0;
 5363|      0|  }
 5364|  1.87M|  return ccc_block_row(ccc_index[c >> 8])[c % 256];
 5365|  1.87M|}
_ZN3ada4idna10sort_marksERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5367|  6.39k|void sort_marks(std::u32string& input) {
 5368|   104k|  for (size_t idx = 1; idx < input.size(); idx++) {
  ------------------
  |  Branch (5368:24): [True: 98.3k, False: 6.39k]
  ------------------
 5369|  98.3k|    uint8_t ccc = get_ccc(input[idx]);
 5370|  98.3k|    if (ccc == 0) {
  ------------------
  |  Branch (5370:9): [True: 73.7k, False: 24.6k]
  ------------------
 5371|  73.7k|      continue;
 5372|  73.7k|    }
 5373|  24.6k|    auto current_character = input[idx];
 5374|  24.6k|    size_t back_idx = idx;
 5375|  36.6k|    while (back_idx != 0 && get_ccc(input[back_idx - 1]) > ccc) {
  ------------------
  |  Branch (5375:12): [True: 36.2k, False: 324]
  |  Branch (5375:29): [True: 11.9k, False: 24.3k]
  ------------------
 5376|  11.9k|      input[back_idx] = input[back_idx - 1];
 5377|  11.9k|      back_idx--;
 5378|  11.9k|    }
 5379|  24.6k|    input[back_idx] = current_character;
 5380|  24.6k|  }
 5381|  6.39k|}
_ZN3ada4idna13decompose_nfcERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5383|  6.39k|void decompose_nfc(std::u32string& input) {
 5384|       |  /**
 5385|       |   * Decompose the domain_name string to Unicode Normalization Form C.
 5386|       |   * @see https://www.unicode.org/reports/tr46/#ProcessingStepDecompose
 5387|       |   */
 5388|  6.39k|  auto [decomposition_needed, additional_elements] =
 5389|  6.39k|      compute_decomposition_length(input);
 5390|  6.39k|  if (decomposition_needed) {
  ------------------
  |  Branch (5390:7): [True: 5.16k, False: 1.23k]
  ------------------
 5391|  5.16k|    decompose(input, additional_elements);
 5392|  5.16k|  }
 5393|  6.39k|  sort_marks(input);
 5394|  6.39k|}
_ZN3ada4idna14is_already_nfcENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5477|  23.2k|bool is_already_nfc(std::u32string_view input) noexcept {
 5478|  23.2k|  if (input.empty()) {
  ------------------
  |  Branch (5478:7): [True: 0, False: 23.2k]
  ------------------
 5479|      0|    return true;
 5480|      0|  }
 5481|  23.2k|  if (!tables_are_ready() && !ensure_tables()) {
  ------------------
  |  Branch (5481:7): [True: 0, False: 23.2k]
  |  Branch (5481:30): [True: 0, False: 0]
  ------------------
 5482|      0|    return false;
 5483|      0|  }
 5484|       |  // 1) Singleton decompositions are never NFC (e.g. U+212B ANGSTROM SIGN).
 5485|       |  //    Multi-code-point decomps are primary composites that are NFC as-is.
 5486|   598k|  for (char32_t c : input) {
  ------------------
  |  Branch (5486:19): [True: 598k, False: 23.2k]
  ------------------
 5487|   598k|    if (canonical_decomp_length(c) == 1) {
  ------------------
  |  Branch (5487:9): [True: 0, False: 598k]
  ------------------
 5488|      0|      return false;
 5489|      0|    }
 5490|   598k|  }
 5491|       |  // 2) Combining marks already in canonical order. prev_ccc carries the
 5492|       |  //    trailing class of the previous character's canonical decomposition so a
 5493|       |  //    composite starter followed by a lower-class mark is caught.
 5494|  23.2k|  uint8_t prev_ccc = 0;
 5495|   586k|  for (char32_t c : input) {
  ------------------
  |  Branch (5495:19): [True: 586k, False: 13.4k]
  ------------------
 5496|   586k|    uint8_t ccc = get_ccc(c);
 5497|   586k|    if (ccc != 0 && prev_ccc > ccc) {
  ------------------
  |  Branch (5497:9): [True: 28.5k, False: 557k]
  |  Branch (5497:21): [True: 9.82k, False: 18.6k]
  ------------------
 5498|  9.82k|      return false;
 5499|  9.82k|    }
 5500|   576k|    prev_ccc = trailing_ccc(c);
 5501|   576k|  }
 5502|       |  // 3) No pairwise composition would apply (including Hangul L+V / LV+T).
 5503|  13.4k|  return !would_compose(input);
 5504|  23.2k|}
_ZN3ada4idna7composeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5506|  6.39k|void compose(std::u32string& input) {
 5507|       |  /**
 5508|       |   * Compose the domain_name string to Unicode Normalization Form C.
 5509|       |   * @see https://www.unicode.org/reports/tr46/#ProcessingStepCompose
 5510|       |   */
 5511|  6.39k|  size_t input_count{0};
 5512|  6.39k|  size_t composition_count{0};
 5513|  77.5k|  for (; input_count < input.size(); input_count++, composition_count++) {
  ------------------
  |  Branch (5513:10): [True: 71.1k, False: 6.39k]
  ------------------
 5514|  71.1k|    input[composition_count] = input[input_count];
 5515|  71.1k|    if (input[input_count] >= hangul_lbase &&
  ------------------
  |  Branch (5515:9): [True: 15.8k, False: 55.2k]
  ------------------
 5516|  15.8k|        input[input_count] < hangul_lbase + hangul_lcount) {
  ------------------
  |  Branch (5516:9): [True: 7.83k, False: 8.02k]
  ------------------
 5517|  7.83k|      if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (5517:11): [True: 7.80k, False: 33]
  ------------------
 5518|  7.80k|          input[input_count + 1] >= hangul_vbase &&
  ------------------
  |  Branch (5518:11): [True: 6.29k, False: 1.50k]
  ------------------
 5519|  6.29k|          input[input_count + 1] < hangul_vbase + hangul_vcount) {
  ------------------
  |  Branch (5519:11): [True: 5.69k, False: 600]
  ------------------
 5520|  5.69k|        input[composition_count] =
 5521|  5.69k|            hangul_sbase +
 5522|  5.69k|            ((input[input_count] - hangul_lbase) * hangul_vcount +
 5523|  5.69k|             input[input_count + 1] - hangul_vbase) *
 5524|  5.69k|                hangul_tcount;
 5525|  5.69k|        input_count++;
 5526|  5.69k|        if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (5526:13): [True: 5.55k, False: 138]
  ------------------
 5527|  5.55k|            input[input_count + 1] > hangul_tbase &&
  ------------------
  |  Branch (5527:13): [True: 3.61k, False: 1.94k]
  ------------------
 5528|  3.61k|            input[input_count + 1] < hangul_tbase + hangul_tcount) {
  ------------------
  |  Branch (5528:13): [True: 3.09k, False: 522]
  ------------------
 5529|  3.09k|          input[composition_count] += input[++input_count] - hangul_tbase;
 5530|  3.09k|        }
 5531|  5.69k|      }
 5532|  63.2k|    } else if (input[input_count] >= hangul_sbase &&
  ------------------
  |  Branch (5532:16): [True: 2.82k, False: 60.4k]
  ------------------
 5533|  2.82k|               input[input_count] < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5533:16): [True: 0, False: 2.82k]
  ------------------
 5534|      0|      if ((input[input_count] - hangul_sbase) % hangul_tcount &&
  ------------------
  |  Branch (5534:11): [True: 0, False: 0]
  ------------------
 5535|      0|          input_count + 1 < input.size() &&
  ------------------
  |  Branch (5535:11): [True: 0, False: 0]
  ------------------
 5536|      0|          input[input_count + 1] > hangul_tbase &&
  ------------------
  |  Branch (5536:11): [True: 0, False: 0]
  ------------------
 5537|      0|          input[input_count + 1] < hangul_tbase + hangul_tcount) {
  ------------------
  |  Branch (5537:11): [True: 0, False: 0]
  ------------------
 5538|      0|        input[composition_count] += input[++input_count] - hangul_tbase;
 5539|      0|      }
 5540|  63.2k|    } else if (input[input_count] < 0x110000) {
  ------------------
  |  Branch (5540:16): [True: 63.2k, False: 0]
  ------------------
 5541|  63.2k|      const uint16_t* composition =
 5542|  63.2k|          composition_block_row(composition_index[input[input_count] >> 8]) +
 5543|  63.2k|          (input[input_count] % 256);
 5544|  63.2k|      size_t initial_composition_count = composition_count;
 5545|  88.1k|      for (int32_t previous_ccc = -1; input_count + 1 < input.size();
  ------------------
  |  Branch (5545:39): [True: 81.9k, False: 6.19k]
  ------------------
 5546|  81.9k|           input_count++) {
 5547|  81.9k|        uint8_t ccc = get_ccc(input[input_count + 1]);
 5548|       |
 5549|  81.9k|        if (composition[1] != composition[0] && previous_ccc < ccc) {
  ------------------
  |  Branch (5549:13): [True: 32.6k, False: 49.2k]
  |  Branch (5549:49): [True: 29.8k, False: 2.79k]
  ------------------
 5550|  29.8k|          int left = composition[0];
 5551|  29.8k|          int right = composition[1];
 5552|  29.8k|          if (left < 0 || right < left ||
  ------------------
  |  Branch (5552:15): [True: 0, False: 29.8k]
  |  Branch (5552:27): [True: 0, False: 29.8k]
  ------------------
 5553|  29.8k|              static_cast<size_t>(right) > composition_data_size) {
  ------------------
  |  Branch (5553:15): [True: 0, False: 29.8k]
  ------------------
 5554|      0|            break;
 5555|      0|          }
 5556|  68.3k|          while (left + 2 < right) {
  ------------------
  |  Branch (5556:18): [True: 38.4k, False: 29.8k]
  ------------------
 5557|  38.4k|            int middle = left + (((right - left) >> 1) & ~1);
 5558|  38.4k|            if (composition_data[static_cast<size_t>(middle)] <=
  ------------------
  |  Branch (5558:17): [True: 20.3k, False: 18.1k]
  ------------------
 5559|  38.4k|                input[input_count + 1]) {
 5560|  20.3k|              left = middle;
 5561|  20.3k|            }
 5562|  38.4k|            if (composition_data[static_cast<size_t>(middle)] >=
  ------------------
  |  Branch (5562:17): [True: 27.2k, False: 11.1k]
  ------------------
 5563|  38.4k|                input[input_count + 1]) {
 5564|  27.2k|              right = middle;
 5565|  27.2k|            }
 5566|  38.4k|          }
 5567|  29.8k|          if (static_cast<size_t>(left + 1) < composition_data_size &&
  ------------------
  |  Branch (5567:15): [True: 29.8k, False: 0]
  ------------------
 5568|  29.8k|              composition_data[static_cast<size_t>(left)] ==
  ------------------
  |  Branch (5568:15): [True: 12.5k, False: 17.3k]
  ------------------
 5569|  29.8k|                  input[input_count + 1]) {
 5570|  12.5k|            char32_t composed = composition_data[static_cast<size_t>(left + 1)];
 5571|  12.5k|            input[initial_composition_count] = composed;
 5572|  12.5k|            composition =
 5573|  12.5k|                composition_block_row(composition_index[composed >> 8]) +
 5574|  12.5k|                (composed % 256);
 5575|  12.5k|            continue;
 5576|  12.5k|          }
 5577|  29.8k|        }
 5578|       |
 5579|  69.4k|        if (ccc == 0) {
  ------------------
  |  Branch (5579:13): [True: 57.0k, False: 12.3k]
  ------------------
 5580|  57.0k|          break;
 5581|  57.0k|        }
 5582|  12.3k|        previous_ccc = ccc;
 5583|  12.3k|        input[++composition_count] = input[input_count + 1];
 5584|  12.3k|      }
 5585|  63.2k|    }
 5586|  71.1k|  }
 5587|       |
 5588|  6.39k|  if (composition_count < input_count) {
  ------------------
  |  Branch (5588:7): [True: 6.07k, False: 327]
  ------------------
 5589|  6.07k|    input.resize(composition_count);
 5590|  6.07k|  }
 5591|  6.39k|}
_ZN3ada4idna9normalizeERNSt3__112basic_stringIDiNS1_11char_traitsIDiEENS1_9allocatorIDiEEEE:
 5593|  6.39k|bool normalize(std::u32string& input) {
 5594|       |  /**
 5595|       |   * Normalize the characters according to IDNA (Unicode Normalization Form C).
 5596|       |   * @see https://www.unicode.org/reports/tr46/#ProcessingStepNormalize
 5597|       |   */
 5598|  6.39k|  if (!ensure_tables() || decomposition_index == nullptr ||
  ------------------
  |  Branch (5598:7): [True: 0, False: 6.39k]
  |  Branch (5598:27): [True: 0, False: 6.39k]
  ------------------
 5599|  6.39k|      composition_index == nullptr) {
  ------------------
  |  Branch (5599:7): [True: 0, False: 6.39k]
  ------------------
 5600|      0|    return false;
 5601|      0|  }
 5602|  6.39k|  if (is_already_nfc(input)) {
  ------------------
  |  Branch (5602:7): [True: 0, False: 6.39k]
  ------------------
 5603|      0|    return true;
 5604|      0|  }
 5605|  6.39k|  decompose_nfc(input);
 5606|  6.39k|  compose(input);
 5607|  6.39k|  return true;
 5608|  6.39k|}
_ZN3ada4idna17punycode_to_utf32ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERNS1_12basic_stringIDiNS3_IDiEENS1_9allocatorIDiEEEE:
 5668|  8.74k|bool punycode_to_utf32(std::string_view input, std::u32string &out) {
 5669|  8.74k|  int32_t written_out{0};
 5670|  8.74k|  out.reserve(out.size() + input.size());
 5671|  8.74k|  uint32_t n = initial_n;
 5672|  8.74k|  int32_t i = 0;
 5673|  8.74k|  int32_t bias = initial_bias;
 5674|       |  // grab ascii content
 5675|  8.74k|  size_t end_of_ascii = input.find_last_of('-');
 5676|  8.74k|  if (end_of_ascii != std::string_view::npos) {
  ------------------
  |  Branch (5676:7): [True: 1.22k, False: 7.51k]
  ------------------
 5677|  9.84k|    for (uint8_t c : input.substr(0, end_of_ascii)) {
  ------------------
  |  Branch (5677:20): [True: 9.84k, False: 1.22k]
  ------------------
 5678|  9.84k|      if (c >= 0x80) {
  ------------------
  |  Branch (5678:11): [True: 0, False: 9.84k]
  ------------------
 5679|      0|        return false;
 5680|      0|      }
 5681|  9.84k|      out.push_back(c);
 5682|  9.84k|      written_out++;
 5683|  9.84k|    }
 5684|  1.22k|    input.remove_prefix(end_of_ascii + 1);
 5685|  1.22k|  }
 5686|  75.5k|  while (!input.empty()) {
  ------------------
  |  Branch (5686:10): [True: 67.1k, False: 8.36k]
  ------------------
 5687|  67.1k|    int32_t oldi = i;
 5688|  67.1k|    int32_t w = 1;
 5689|  95.7k|    for (int32_t k = base;; k += base) {
 5690|  95.7k|      if (input.empty()) {
  ------------------
  |  Branch (5690:11): [True: 183, False: 95.5k]
  ------------------
 5691|    183|        return false;
 5692|    183|      }
 5693|  95.5k|      uint8_t code_point = input.front();
 5694|  95.5k|      input.remove_prefix(1);
 5695|  95.5k|      int32_t digit = char_to_digit_value(code_point);
 5696|  95.5k|      if (digit < 0) {
  ------------------
  |  Branch (5696:11): [True: 72, False: 95.4k]
  ------------------
 5697|     72|        return false;
 5698|     72|      }
 5699|  95.4k|      if (digit > (0x7fffffff - i) / w) {
  ------------------
  |  Branch (5699:11): [True: 21, False: 95.4k]
  ------------------
 5700|     21|        return false;
 5701|     21|      }
 5702|  95.4k|      i = i + digit * w;
 5703|  95.4k|      int32_t t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
  ------------------
  |  Branch (5703:19): [True: 21.5k, False: 73.9k]
  |  Branch (5703:38): [True: 68.9k, False: 4.93k]
  ------------------
 5704|  95.4k|      if (digit < t) {
  ------------------
  |  Branch (5704:11): [True: 66.9k, False: 28.5k]
  ------------------
 5705|  66.9k|        break;
 5706|  66.9k|      }
 5707|  28.5k|      if (w > 0x7fffffff / (base - t)) {
  ------------------
  |  Branch (5707:11): [True: 0, False: 28.5k]
  ------------------
 5708|      0|        return false;
 5709|      0|      }
 5710|  28.5k|      w = w * (base - t);
 5711|  28.5k|    }
 5712|  66.9k|    bias = adapt(i - oldi, written_out + 1, oldi == 0);
 5713|  66.9k|    if (i / (written_out + 1) > int32_t(0x7fffffff - n)) {
  ------------------
  |  Branch (5713:9): [True: 99, False: 66.8k]
  ------------------
 5714|     99|      return false;
 5715|     99|    }
 5716|  66.8k|    n = n + i / (written_out + 1);
 5717|  66.8k|    i = i % (written_out + 1);
 5718|  66.8k|    if (n < 0x80) {
  ------------------
  |  Branch (5718:9): [True: 0, False: 66.8k]
  ------------------
 5719|      0|      return false;
 5720|      0|    }
 5721|  66.8k|    out.insert(out.begin() + i, n);
 5722|  66.8k|    written_out++;
 5723|  66.8k|    ++i;
 5724|  66.8k|  }
 5725|       |  // See https://github.com/whatwg/url/issues/803
 5726|       |  // Reject labels whose decoded form begins with "xn--" (double-encoded ACE).
 5727|  8.36k|  if (out.size() >= 4 && out[0] == U'x' && out[1] == U'n' && out[2] == U'-' &&
  ------------------
  |  Branch (5727:7): [True: 2.35k, False: 6.00k]
  |  Branch (5727:26): [True: 432, False: 1.92k]
  |  Branch (5727:44): [True: 249, False: 183]
  |  Branch (5727:62): [True: 153, False: 96]
  ------------------
 5728|    153|      out[3] == U'-') {
  ------------------
  |  Branch (5728:7): [True: 9, False: 144]
  ------------------
 5729|      9|    return false;
 5730|      9|  }
 5731|  8.35k|  return true;
 5732|  8.36k|}
_ZN3ada4idna17utf32_to_punycodeENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEERNS1_12basic_stringIcNS3_IcEENS1_9allocatorIcEEEE:
 5812|  37.5k|bool utf32_to_punycode(std::u32string_view input, std::string &out) {
 5813|  37.5k|  out.reserve(input.size() + out.size());
 5814|  37.5k|  uint32_t n = initial_n;
 5815|  37.5k|  int32_t d = 0;
 5816|  37.5k|  int32_t bias = initial_bias;
 5817|  37.5k|  size_t h = 0;
 5818|       |  // first push the ascii content
 5819|   239k|  for (uint32_t c : input) {
  ------------------
  |  Branch (5819:19): [True: 239k, False: 37.5k]
  ------------------
 5820|   239k|    if (c < 0x80) {
  ------------------
  |  Branch (5820:9): [True: 30.3k, False: 209k]
  ------------------
 5821|  30.3k|      ++h;
 5822|  30.3k|      out.push_back(char(c));
 5823|  30.3k|    }
 5824|   239k|    if (c > 0x10ffff || (c >= 0xd800 && c < 0xe000)) {
  ------------------
  |  Branch (5824:9): [True: 0, False: 239k]
  |  Branch (5824:26): [True: 1.67k, False: 238k]
  |  Branch (5824:41): [True: 0, False: 1.67k]
  ------------------
 5825|      0|      return false;
 5826|      0|    }
 5827|   239k|  }
 5828|  37.5k|  size_t b = h;
 5829|  37.5k|  if (b > 0) {
  ------------------
  |  Branch (5829:7): [True: 5.67k, False: 31.8k]
  ------------------
 5830|  5.67k|    out.push_back('-');
 5831|  5.67k|  }
 5832|   166k|  while (h < input.size()) {
  ------------------
  |  Branch (5832:10): [True: 128k, False: 37.5k]
  ------------------
 5833|   128k|    uint32_t m = 0x10FFFF;
 5834|  1.61M|    for (auto code_point : input) {
  ------------------
  |  Branch (5834:26): [True: 1.61M, False: 128k]
  ------------------
 5835|  1.61M|      if (code_point >= n && code_point < m) m = code_point;
  ------------------
  |  Branch (5835:11): [True: 770k, False: 848k]
  |  Branch (5835:30): [True: 221k, False: 548k]
  ------------------
 5836|  1.61M|    }
 5837|       |
 5838|   128k|    if ((m - n) > (0x7fffffff - d) / (h + 1)) {
  ------------------
  |  Branch (5838:9): [True: 0, False: 128k]
  ------------------
 5839|      0|      return false;
 5840|      0|    }
 5841|   128k|    d = d + int32_t((m - n) * (h + 1));
 5842|   128k|    n = m;
 5843|  1.61M|    for (auto c : input) {
  ------------------
  |  Branch (5843:17): [True: 1.61M, False: 128k]
  ------------------
 5844|  1.61M|      if (c < n) {
  ------------------
  |  Branch (5844:11): [True: 848k, False: 770k]
  ------------------
 5845|   848k|        if (d == 0x7fffffff) {
  ------------------
  |  Branch (5845:13): [True: 0, False: 848k]
  ------------------
 5846|      0|          return false;
 5847|      0|        }
 5848|   848k|        ++d;
 5849|   848k|      }
 5850|  1.61M|      if (c == n) {
  ------------------
  |  Branch (5850:11): [True: 209k, False: 1.40M]
  ------------------
 5851|   209k|        int32_t q = d;
 5852|   505k|        for (int32_t k = base;; k += base) {
 5853|   505k|          int32_t t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
  ------------------
  |  Branch (5853:23): [True: 134k, False: 371k]
  |  Branch (5853:42): [True: 286k, False: 85.5k]
  ------------------
 5854|       |
 5855|   505k|          if (q < t) {
  ------------------
  |  Branch (5855:15): [True: 209k, False: 296k]
  ------------------
 5856|   209k|            break;
 5857|   209k|          }
 5858|   296k|          out.push_back(digit_to_char(t + ((q - t) % (base - t))));
 5859|   296k|          q = (q - t) / (base - t);
 5860|   296k|        }
 5861|   209k|        out.push_back(digit_to_char(q));
 5862|   209k|        bias = adapt(d, int32_t(h + 1), h == b);
 5863|   209k|        d = 0;
 5864|   209k|        ++h;
 5865|   209k|      }
 5866|  1.61M|    }
 5867|   128k|    ++d;
 5868|   128k|    ++n;
 5869|   128k|  }
 5870|  37.5k|  return true;
 5871|  37.5k|}
_ZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5955|  46.5k|bool is_label_valid(const std::u32string_view label) {
 5956|  46.5k|  if (label.empty()) {
  ------------------
  |  Branch (5956:7): [True: 0, False: 46.5k]
  ------------------
 5957|      0|    return true;
 5958|      0|  }
 5959|  46.5k|  if (!ensure_tables() || combining_ranges == nullptr || dir_start == nullptr ||
  ------------------
  |  Branch (5959:7): [True: 0, False: 46.5k]
  |  Branch (5959:27): [True: 0, False: 46.5k]
  |  Branch (5959:58): [True: 0, False: 46.5k]
  ------------------
 5960|  46.5k|      dir_final == nullptr || dir_value == nullptr) {
  ------------------
  |  Branch (5960:7): [True: 0, False: 46.5k]
  |  Branch (5960:31): [True: 0, False: 46.5k]
  ------------------
 5961|      0|    return false;
 5962|      0|  }
 5963|       |
 5964|       |  ///////////////
 5965|       |  // We have a normalization step which ensures that we are in NFC.
 5966|       |  // If we receive punycode, we normalize and check that the normalized
 5967|       |  // version matches the original.
 5968|       |  // --------------------------------------
 5969|       |  // The label must be in Unicode Normalization Form NFC.
 5970|       |
 5971|       |  // Current URL standard indicatest that CheckHyphens is set to false.
 5972|       |  // ---------------------------------------
 5973|       |  // If CheckHyphens, the label must not contain a U+002D HYPHEN-MINUS character
 5974|       |  // in both the third and fourth positions. If CheckHyphens, the label must
 5975|       |  // neither begin nor end with a U+002D HYPHEN-MINUS character.
 5976|       |
 5977|       |  // This is not necessary because we segment the
 5978|       |  // labels by '.'.
 5979|       |  // ---------------------------------------
 5980|       |  // The label must not contain a U+002E ( . ) FULL STOP.
 5981|       |  // if (label.find('.') != std::string_view::npos) return false;
 5982|       |
 5983|       |  // The label must not begin with a combining mark.
 5984|       |  // Range membership via lower_bound on range end.
 5985|  46.5k|  const std::span<const uint32_t[2]> comb_span{combining_ranges,
 5986|  46.5k|                                               combining_range_count};
 5987|  46.5k|  const auto comb_it = std::ranges::lower_bound(
 5988|  46.5k|      comb_span, label.front(), {}, [](const auto& range) { return range[1]; });
 5989|  46.5k|  if (comb_it != comb_span.end() && label.front() >= (*comb_it)[0]) {
  ------------------
  |  Branch (5989:7): [True: 46.5k, False: 0]
  |  Branch (5989:7): [True: 252, False: 46.3k]
  |  Branch (5989:37): [True: 252, False: 46.3k]
  ------------------
 5990|    252|    return false;
 5991|    252|  }
 5992|       |  // We verify this next step as part of the mapping:
 5993|       |  // ---------------------------------------------
 5994|       |  // Each code point in the label must only have certain status values
 5995|       |  // according to Section 5, IDNA Mapping Table:
 5996|       |  // - For Transitional Processing, each value must be valid.
 5997|       |  // - For Nontransitional Processing, each value must be either valid or
 5998|       |  // deviation.
 5999|       |
 6000|       |  // If CheckJoiners, the label must satisfy the ContextJ rules from Appendix
 6001|       |  // A, in The Unicode Code Points and Internationalized Domain Names for
 6002|       |  // Applications (IDNA) [IDNA2008].
 6003|  46.3k|  constexpr static uint32_t virama[] = {
 6004|  46.3k|      0x094D,  0x09CD,  0x0A4D,  0x0ACD,  0x0B4D,  0x0BCD,  0x0C4D,  0x0CCD,
 6005|  46.3k|      0x0D3B,  0x0D3C,  0x0D4D,  0x0DCA,  0x0E3A,  0x0EBA,  0x0F84,  0x1039,
 6006|  46.3k|      0x103A,  0x1714,  0x1734,  0x17D2,  0x1A60,  0x1B44,  0x1BAA,  0x1BAB,
 6007|  46.3k|      0x1BF2,  0x1BF3,  0x2D7F,  0xA806,  0xA82C,  0xA8C4,  0xA953,  0xA9C0,
 6008|  46.3k|      0xAAF6,  0xABED,  0x10A3F, 0x11046, 0x1107F, 0x110B9, 0x11133, 0x11134,
 6009|  46.3k|      0x111C0, 0x11235, 0x112EA, 0x1134D, 0x11442, 0x114C2, 0x115BF, 0x1163F,
 6010|  46.3k|      0x116B6, 0x1172B, 0x11839, 0x1193D, 0x1193E, 0x119E0, 0x11A34, 0x11A47,
 6011|  46.3k|      0x11A99, 0x11C3F, 0x11D44, 0x11D45, 0x11D97};
 6012|  46.3k|  constexpr static uint32_t R[] = {
 6013|  46.3k|      0x622, 0x623, 0x624, 0x625, 0x627, 0x629, 0x62f, 0x630, 0x631,
 6014|  46.3k|      0x632, 0x648, 0x671, 0x672, 0x673, 0x675, 0x676, 0x677, 0x688,
 6015|  46.3k|      0x689, 0x68a, 0x68b, 0x68c, 0x68d, 0x68e, 0x68f, 0x690, 0x691,
 6016|  46.3k|      0x692, 0x693, 0x694, 0x695, 0x696, 0x697, 0x698, 0x699, 0x6c0,
 6017|  46.3k|      0x6c3, 0x6c4, 0x6c5, 0x6c6, 0x6c7, 0x6c8, 0x6c9, 0x6ca, 0x6cb,
 6018|  46.3k|      0x6cd, 0x6cf, 0x6d2, 0x6d3, 0x6d5, 0x6ee, 0x6ef, 0x710, 0x715,
 6019|  46.3k|      0x716, 0x717, 0x718, 0x719, 0x71e, 0x728, 0x72a, 0x72c, 0x72f,
 6020|  46.3k|      0x74d, 0x759, 0x75a, 0x75b, 0x854, 0x8aa, 0x8ab, 0x8ac};
 6021|  46.3k|  constexpr static uint32_t L[] = {0xa872};
 6022|  46.3k|  constexpr static uint32_t D[] = {
 6023|  46.3k|      0x620,  0x626,  0x628,  0x62a,  0x62b,  0x62c,  0x62d,  0x62e,  0x633,
 6024|  46.3k|      0x634,  0x635,  0x636,  0x637,  0x638,  0x639,  0x63a,  0x63b,  0x63c,
 6025|  46.3k|      0x63d,  0x63e,  0x63f,  0x641,  0x642,  0x643,  0x644,  0x645,  0x646,
 6026|  46.3k|      0x647,  0x649,  0x64a,  0x66e,  0x66f,  0x678,  0x679,  0x67a,  0x67b,
 6027|  46.3k|      0x67c,  0x67d,  0x67e,  0x67f,  0x680,  0x681,  0x682,  0x683,  0x684,
 6028|  46.3k|      0x685,  0x686,  0x687,  0x69a,  0x69b,  0x69c,  0x69d,  0x69e,  0x69f,
 6029|  46.3k|      0x6a0,  0x6a1,  0x6a2,  0x6a3,  0x6a4,  0x6a5,  0x6a6,  0x6a7,  0x6a8,
 6030|  46.3k|      0x6a9,  0x6aa,  0x6ab,  0x6ac,  0x6ad,  0x6ae,  0x6af,  0x6b0,  0x6b1,
 6031|  46.3k|      0x6b2,  0x6b3,  0x6b4,  0x6b5,  0x6b6,  0x6b7,  0x6b8,  0x6b9,  0x6ba,
 6032|  46.3k|      0x6bb,  0x6bc,  0x6bd,  0x6be,  0x6bf,  0x6c1,  0x6c2,  0x6cc,  0x6ce,
 6033|  46.3k|      0x6d0,  0x6d1,  0x6fa,  0x6fb,  0x6fc,  0x6ff,  0x712,  0x713,  0x714,
 6034|  46.3k|      0x71a,  0x71b,  0x71c,  0x71d,  0x71f,  0x720,  0x721,  0x722,  0x723,
 6035|  46.3k|      0x724,  0x725,  0x726,  0x727,  0x729,  0x72b,  0x72d,  0x72e,  0x74e,
 6036|  46.3k|      0x74f,  0x750,  0x751,  0x752,  0x753,  0x754,  0x755,  0x756,  0x757,
 6037|  46.3k|      0x758,  0x75c,  0x75d,  0x75e,  0x75f,  0x760,  0x761,  0x762,  0x763,
 6038|  46.3k|      0x764,  0x765,  0x766,  0x850,  0x851,  0x852,  0x853,  0x855,  0x8a0,
 6039|  46.3k|      0x8a2,  0x8a3,  0x8a4,  0x8a5,  0x8a6,  0x8a7,  0x8a8,  0x8a9,  0x1807,
 6040|  46.3k|      0x1820, 0x1821, 0x1822, 0x1823, 0x1824, 0x1825, 0x1826, 0x1827, 0x1828,
 6041|  46.3k|      0x1829, 0x182a, 0x182b, 0x182c, 0x182d, 0x182e, 0x182f, 0x1830, 0x1831,
 6042|  46.3k|      0x1832, 0x1833, 0x1834, 0x1835, 0x1836, 0x1837, 0x1838, 0x1839, 0x183a,
 6043|  46.3k|      0x183b, 0x183c, 0x183d, 0x183e, 0x183f, 0x1840, 0x1841, 0x1842, 0x1843,
 6044|  46.3k|      0x1844, 0x1845, 0x1846, 0x1847, 0x1848, 0x1849, 0x184a, 0x184b, 0x184c,
 6045|  46.3k|      0x184d, 0x184e, 0x184f, 0x1850, 0x1851, 0x1852, 0x1853, 0x1854, 0x1855,
 6046|  46.3k|      0x1856, 0x1857, 0x1858, 0x1859, 0x185a, 0x185b, 0x185c, 0x185d, 0x185e,
 6047|  46.3k|      0x185f, 0x1860, 0x1861, 0x1862, 0x1863, 0x1864, 0x1865, 0x1866, 0x1867,
 6048|  46.3k|      0x1868, 0x1869, 0x186a, 0x186b, 0x186c, 0x186d, 0x186e, 0x186f, 0x1870,
 6049|  46.3k|      0x1871, 0x1872, 0x1873, 0x1874, 0x1875, 0x1876, 0x1877, 0x1887, 0x1888,
 6050|  46.3k|      0x1889, 0x188a, 0x188b, 0x188c, 0x188d, 0x188e, 0x188f, 0x1890, 0x1891,
 6051|  46.3k|      0x1892, 0x1893, 0x1894, 0x1895, 0x1896, 0x1897, 0x1898, 0x1899, 0x189a,
 6052|  46.3k|      0x189b, 0x189c, 0x189d, 0x189e, 0x189f, 0x18a0, 0x18a1, 0x18a2, 0x18a3,
 6053|  46.3k|      0x18a4, 0x18a5, 0x18a6, 0x18a7, 0x18a8, 0x18aa, 0xa840, 0xa841, 0xa842,
 6054|  46.3k|      0xa843, 0xa844, 0xa845, 0xa846, 0xa847, 0xa848, 0xa849, 0xa84a, 0xa84b,
 6055|  46.3k|      0xa84c, 0xa84d, 0xa84e, 0xa84f, 0xa850, 0xa851, 0xa852, 0xa853, 0xa854,
 6056|  46.3k|      0xa855, 0xa856, 0xa857, 0xa858, 0xa859, 0xa85a, 0xa85b, 0xa85c, 0xa85d,
 6057|  46.3k|      0xa85e, 0xa85f, 0xa860, 0xa861, 0xa862, 0xa863, 0xa864, 0xa865, 0xa866,
 6058|  46.3k|      0xa867, 0xa868, 0xa869, 0xa86a, 0xa86b, 0xa86c, 0xa86d, 0xa86e, 0xa86f,
 6059|  46.3k|      0xa870, 0xa871};
 6060|       |
 6061|   313k|  for (size_t i = 0; i < label.size(); i++) {
  ------------------
  |  Branch (6061:22): [True: 270k, False: 43.1k]
  ------------------
 6062|   270k|    uint32_t c = label[i];
 6063|   270k|    if (c == 0x200c) {
  ------------------
  |  Branch (6063:9): [True: 2.28k, False: 267k]
  ------------------
 6064|  2.28k|      if (i > 0) {
  ------------------
  |  Branch (6064:11): [True: 2.27k, False: 9]
  ------------------
 6065|  2.27k|        if (std::ranges::binary_search(virama, label[i - 1])) {
  ------------------
  |  Branch (6065:13): [True: 84, False: 2.18k]
  ------------------
 6066|     84|          return true;
 6067|     84|        }
 6068|  2.27k|      }
 6069|  2.19k|      if ((i == 0) || (i + 1 >= label.size())) {
  ------------------
  |  Branch (6069:11): [True: 9, False: 2.18k]
  |  Branch (6069:23): [True: 180, False: 2.00k]
  ------------------
 6070|    189|        return false;
 6071|    189|      }
 6072|       |      // we go backward looking for L or D
 6073|  2.00k|      auto is_l_or_d = [](uint32_t code) {
 6074|  2.00k|        return std::ranges::binary_search(L, code) ||
 6075|  2.00k|               std::ranges::binary_search(D, code);
 6076|  2.00k|      };
 6077|  2.00k|      auto is_r_or_d = [](uint32_t code) {
 6078|  2.00k|        return std::ranges::binary_search(R, code) ||
 6079|  2.00k|               std::ranges::binary_search(D, code);
 6080|  2.00k|      };
 6081|  2.00k|      std::u32string_view before = label.substr(0, i);
 6082|  2.00k|      std::u32string_view after = label.substr(i + 1);
 6083|  2.00k|      return (std::find_if(before.begin(), before.end(), is_l_or_d) !=
  ------------------
  |  Branch (6083:14): [True: 1.83k, False: 177]
  ------------------
 6084|  2.00k|              before.end()) &&
 6085|  1.83k|             (std::find_if(after.begin(), after.end(), is_r_or_d) !=
  ------------------
  |  Branch (6085:14): [True: 1.54k, False: 282]
  ------------------
 6086|  1.83k|              after.end());
 6087|   267k|    } else if (c == 0x200d) {
  ------------------
  |  Branch (6087:16): [True: 900, False: 266k]
  ------------------
 6088|    900|      if (i > 0) {
  ------------------
  |  Branch (6088:11): [True: 879, False: 21]
  ------------------
 6089|    879|        if (std::ranges::binary_search(virama, label[i - 1])) {
  ------------------
  |  Branch (6089:13): [True: 738, False: 141]
  ------------------
 6090|    738|          return true;
 6091|    738|        }
 6092|    879|      }
 6093|    162|      return false;
 6094|    900|    }
 6095|   270k|  }
 6096|       |
 6097|       |  // If CheckBidi, and if the domain name is a  Bidi domain name, then the label
 6098|       |  // must satisfy all six of the numbered conditions in [IDNA2008] RFC 5893,
 6099|       |  // Section 2.
 6100|       |
 6101|       |  // The following rule, consisting of six conditions, applies to labels
 6102|       |  // in Bidi domain names.  The requirements that this rule satisfies are
 6103|       |  // described in Section 3.  All of the conditions must be satisfied for
 6104|       |  // the rule to be satisfied.
 6105|       |  //
 6106|       |  //  1.  The first character must be a character with Bidi property L, R,
 6107|       |  //     or AL.  If it has the R or AL property, it is an RTL label; if it
 6108|       |  //     has the L property, it is an LTR label.
 6109|       |  //
 6110|       |  //  2.  In an RTL label, only characters with the Bidi properties R, AL,
 6111|       |  //      AN, EN, ES, CS, ET, ON, BN, or NSM are allowed.
 6112|       |  //
 6113|       |  //   3.  In an RTL label, the end of the label must be a character with
 6114|       |  //       Bidi property R, AL, EN, or AN, followed by zero or more
 6115|       |  //       characters with Bidi property NSM.
 6116|       |  //
 6117|       |  //   4.  In an RTL label, if an EN is present, no AN may be present, and
 6118|       |  //       vice versa.
 6119|       |  //
 6120|       |  //  5.  In an LTR label, only characters with the Bidi properties L, EN,
 6121|       |  //       ES, CS, ET, ON, BN, or NSM are allowed.
 6122|       |  //
 6123|       |  //   6.  In an LTR label, the end of the label must be a character with
 6124|       |  //       Bidi property L or EN, followed by zero or more characters with
 6125|       |  //       Bidi property NSM.
 6126|       |
 6127|  43.1k|  size_t last_non_nsm_char = find_last_not_of_nsm(label);
 6128|  43.1k|  if (last_non_nsm_char == std::u32string_view::npos) {
  ------------------
  |  Branch (6128:7): [True: 0, False: 43.1k]
  ------------------
 6129|      0|    return false;
 6130|      0|  }
 6131|       |
 6132|       |  // A "Bidi domain name" is a domain name that contains at least one RTL label.
 6133|       |  // The following rule, consisting of six conditions, applies to labels in Bidi
 6134|       |  // domain names.
 6135|  43.1k|  if (is_rtl_label(label)) {
  ------------------
  |  Branch (6135:7): [True: 6.91k, False: 36.2k]
  ------------------
 6136|       |    // The first character must be a character with Bidi property L, R,
 6137|       |    // or AL. If it has the R or AL property, it is an RTL label; if it
 6138|       |    // has the L property, it is an LTR label.
 6139|       |
 6140|  6.91k|    if (find_direction(label[0]) == direction::L) {
  ------------------
  |  Branch (6140:9): [True: 519, False: 6.39k]
  ------------------
 6141|       |      // Eval as LTR
 6142|       |
 6143|       |      // In an LTR label, only characters with the Bidi properties L, EN,
 6144|       |      // ES, CS, ET, ON, BN, or NSM are allowed.
 6145|  11.4k|      for (size_t i = 0; i <= last_non_nsm_char; i++) {
  ------------------
  |  Branch (6145:26): [True: 11.4k, False: 0]
  ------------------
 6146|  11.4k|        const direction d = find_direction(label[i]);
 6147|  11.4k|        if (!(d == direction::L || d == direction::EN || d == direction::ES ||
  ------------------
  |  Branch (6147:15): [True: 2.93k, False: 8.47k]
  |  Branch (6147:36): [True: 1.40k, False: 7.06k]
  |  Branch (6147:58): [True: 1.09k, False: 5.97k]
  ------------------
 6148|  5.97k|              d == direction::CS || d == direction::ET || d == direction::ON ||
  ------------------
  |  Branch (6148:15): [True: 1.07k, False: 4.89k]
  |  Branch (6148:37): [True: 990, False: 3.90k]
  |  Branch (6148:59): [True: 1.17k, False: 2.73k]
  ------------------
 6149|  2.73k|              d == direction::BN || d == direction::NSM)) {
  ------------------
  |  Branch (6149:15): [True: 1.21k, False: 1.52k]
  |  Branch (6149:37): [True: 1.00k, False: 519]
  ------------------
 6150|    519|          return false;
 6151|    519|        }
 6152|  11.4k|      }
 6153|       |
 6154|      0|      const direction last_dir = find_direction(label[last_non_nsm_char]);
 6155|      0|      if (!(last_dir == direction::L || last_dir == direction::EN)) {
  ------------------
  |  Branch (6155:13): [True: 0, False: 0]
  |  Branch (6155:41): [True: 0, False: 0]
  ------------------
 6156|      0|        return false;
 6157|      0|      }
 6158|       |
 6159|      0|      return true;
 6160|       |
 6161|  6.39k|    } else {
 6162|       |      // Eval as RTL
 6163|       |
 6164|       |      // The first character must be R or AL; a leading AN (or any other
 6165|       |      // direction) does not start a valid RTL label.
 6166|  6.39k|      const direction first_dir = find_direction(label[0]);
 6167|  6.39k|      if (first_dir != direction::R && first_dir != direction::AL) {
  ------------------
  |  Branch (6167:11): [True: 5.08k, False: 1.31k]
  |  Branch (6167:40): [True: 90, False: 4.99k]
  ------------------
 6168|     90|        return false;
 6169|     90|      }
 6170|       |
 6171|  6.30k|      bool has_an = false;
 6172|  6.30k|      bool has_en = false;
 6173|  29.8k|      for (size_t i = 0; i <= last_non_nsm_char; i++) {
  ------------------
  |  Branch (6173:26): [True: 24.0k, False: 5.82k]
  ------------------
 6174|  24.0k|        const direction d = find_direction(label[i]);
 6175|       |
 6176|       |        // In an RTL label, if an EN is present, no AN may be present, and vice
 6177|       |        // versa.
 6178|  24.0k|        if ((d == direction::EN && ((has_en = true) && has_an)) ||
  ------------------
  |  Branch (6178:14): [True: 2.02k, False: 22.0k]
  |  Branch (6178:37): [True: 2.02k, False: 0]
  |  Branch (6178:56): [True: 9, False: 2.01k]
  ------------------
 6179|  24.0k|            (d == direction::AN && ((has_an = true) && has_en))) {
  ------------------
  |  Branch (6179:14): [True: 1.45k, False: 22.6k]
  |  Branch (6179:37): [True: 1.45k, False: 0]
  |  Branch (6179:56): [True: 9, False: 1.44k]
  ------------------
 6180|     18|          return false;
 6181|     18|        }
 6182|       |
 6183|  24.0k|        if (!(d == direction::R || d == direction::AL || d == direction::AN ||
  ------------------
  |  Branch (6183:15): [True: 2.47k, False: 21.5k]
  |  Branch (6183:36): [True: 10.2k, False: 11.2k]
  |  Branch (6183:58): [True: 1.44k, False: 9.83k]
  ------------------
 6184|  9.83k|              d == direction::EN || d == direction::ES || d == direction::CS ||
  ------------------
  |  Branch (6184:15): [True: 2.01k, False: 7.82k]
  |  Branch (6184:37): [True: 993, False: 6.82k]
  |  Branch (6184:59): [True: 1.04k, False: 5.78k]
  ------------------
 6185|  5.78k|              d == direction::ET || d == direction::ON || d == direction::BN ||
  ------------------
  |  Branch (6185:15): [True: 1.08k, False: 4.69k]
  |  Branch (6185:37): [True: 1.21k, False: 3.48k]
  |  Branch (6185:59): [True: 1.61k, False: 1.86k]
  ------------------
 6186|  1.86k|              d == direction::NSM)) {
  ------------------
  |  Branch (6186:15): [True: 1.63k, False: 234]
  ------------------
 6187|    234|          return false;
 6188|    234|        }
 6189|       |
 6190|  23.8k|        if (i == last_non_nsm_char &&
  ------------------
  |  Branch (6190:13): [True: 6.05k, False: 17.7k]
  ------------------
 6191|  6.05k|            !(d == direction::R || d == direction::AL || d == direction::AN ||
  ------------------
  |  Branch (6191:15): [True: 744, False: 5.30k]
  |  Branch (6191:36): [True: 4.05k, False: 1.25k]
  |  Branch (6191:58): [True: 498, False: 756]
  ------------------
 6192|    756|              d == direction::EN)) {
  ------------------
  |  Branch (6192:15): [True: 528, False: 228]
  ------------------
 6193|    228|          return false;
 6194|    228|        }
 6195|  23.8k|      }
 6196|       |
 6197|  5.82k|      return true;
 6198|  6.30k|    }
 6199|  6.91k|  }
 6200|       |
 6201|  36.2k|  return true;
 6202|  43.1k|}
_ZN3ada4idna8to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 6359|  19.7k|[[nodiscard]] bool to_ascii(std::string_view ut8_string, std::string& out) {
 6360|  19.7k|  out.clear();
 6361|  19.7k|  if (ut8_string.size() > max_domain_input_bytes) {
  ------------------
  |  Branch (6361:7): [True: 0, False: 19.7k]
  ------------------
 6362|      0|    return false;
 6363|      0|  }
 6364|  19.7k|  if (is_ascii(ut8_string)) {
  ------------------
  |  Branch (6364:7): [True: 6.09k, False: 13.6k]
  ------------------
 6365|  6.09k|    from_ascii_to_ascii(ut8_string, out);
 6366|  6.09k|    return true;
 6367|  6.09k|  }
 6368|       |
 6369|       |#ifdef ADA_USE_SIMDUTF
 6370|       |  size_t utf32_length =
 6371|       |      simdutf::utf32_length_from_utf8(ut8_string.data(), ut8_string.size());
 6372|       |  if (utf32_length == 0 && !ut8_string.empty()) {
 6373|       |    return false;
 6374|       |  }
 6375|       |  std::u32string working(utf32_length, U'\0');
 6376|       |  size_t actual_utf32_length = simdutf::convert_utf8_to_utf32(
 6377|       |      ut8_string.data(), ut8_string.size(), working.data());
 6378|       |#else
 6379|  13.6k|  size_t utf32_length =
 6380|  13.6k|      ada::idna::utf32_length_from_utf8(ut8_string.data(), ut8_string.size());
 6381|  13.6k|  if (utf32_length == 0 && !ut8_string.empty()) {
  ------------------
  |  Branch (6381:7): [True: 117, False: 13.5k]
  |  Branch (6381:28): [True: 117, False: 0]
  ------------------
 6382|    117|    return false;
 6383|    117|  }
 6384|  13.5k|  std::u32string working(utf32_length, U'\0');
 6385|  13.5k|  size_t actual_utf32_length = ada::idna::utf8_to_utf32(
 6386|  13.5k|      ut8_string.data(), ut8_string.size(), working.data());
 6387|  13.5k|#endif
 6388|  13.5k|  if (actual_utf32_length == 0 || actual_utf32_length != utf32_length) {
  ------------------
  |  Branch (6388:7): [True: 927, False: 12.6k]
  |  Branch (6388:35): [True: 0, False: 12.6k]
  ------------------
 6389|    927|    return false;
 6390|    927|  }
 6391|  12.6k|  working.resize(actual_utf32_length);
 6392|       |
 6393|       |  // Map into a second buffer with exact sizing (no growth reallocs).
 6394|  12.6k|  std::u32string mapped;
 6395|  12.6k|  if (!ada::idna::map(working, mapped)) {
  ------------------
  |  Branch (6395:7): [True: 153, False: 12.4k]
  ------------------
 6396|    153|    return false;
 6397|    153|  }
 6398|       |  // Drop UTF-32 input; reuse `working` as scratch for ACE validation below.
 6399|  12.4k|  working.clear();
 6400|       |
 6401|       |  // Skip NFC when already normalized (ASCII is a fast subset of this check).
 6402|  12.4k|  if (!is_ascii(mapped) && !is_already_nfc(mapped)) {
  ------------------
  |  Branch (6402:7): [True: 9.49k, False: 2.97k]
  |  Branch (6402:28): [True: 1.64k, False: 7.85k]
  ------------------
 6403|  1.64k|    if (!normalize(mapped)) {
  ------------------
  |  Branch (6403:9): [True: 0, False: 1.64k]
  ------------------
 6404|      0|      return false;
 6405|      0|    }
 6406|  1.64k|  }
 6407|       |
 6408|       |  // Estimate ASCII output size (punycode may expand non-ASCII labels).
 6409|  12.4k|  out.reserve(mapped.size() + 8);
 6410|       |
 6411|       |  // Walk labels with a single pointer scan (no repeated string::find).
 6412|  12.4k|  const char32_t* p = mapped.data();
 6413|  12.4k|  const char32_t* const end = p + mapped.size();
 6414|  12.4k|  std::u32string post_map;
 6415|       |
 6416|  65.6k|  while (p < end) {
  ------------------
  |  Branch (6416:10): [True: 57.1k, False: 8.55k]
  ------------------
 6417|  57.1k|    const char32_t* label_begin = p;
 6418|   572k|    while (p < end && *p != U'.') {
  ------------------
  |  Branch (6418:12): [True: 560k, False: 11.8k]
  |  Branch (6418:23): [True: 515k, False: 45.2k]
  ------------------
 6419|   515k|      ++p;
 6420|   515k|    }
 6421|  57.1k|    const size_t label_size = static_cast<size_t>(p - label_begin);
 6422|  57.1k|    std::u32string_view label_view(label_begin, label_size);
 6423|  57.1k|    const bool is_last_label = (p == end);
 6424|  57.1k|    if (p < end) {
  ------------------
  |  Branch (6424:9): [True: 45.2k, False: 11.8k]
  ------------------
 6425|  45.2k|      ++p;  // skip dot
 6426|  45.2k|    }
 6427|       |
 6428|  57.1k|    if (label_size == 0) {
  ------------------
  |  Branch (6428:9): [True: 3.13k, False: 53.9k]
  ------------------
 6429|       |      // empty label
 6430|  53.9k|    } else if (is_ace_prefix(label_view)) {
  ------------------
  |  Branch (6430:16): [True: 8.79k, False: 45.1k]
  ------------------
 6431|   143k|      for (char32_t c : label_view) {
  ------------------
  |  Branch (6431:23): [True: 143k, False: 8.74k]
  ------------------
 6432|   143k|        if (c >= 0x80) {
  ------------------
  |  Branch (6432:13): [True: 48, False: 143k]
  ------------------
 6433|     48|          out.clear();
 6434|     48|          return false;
 6435|     48|        }
 6436|   143k|      }
 6437|  8.74k|      append_ascii_label(out, label_view);
 6438|  8.74k|      std::string_view puny_segment_ascii(
 6439|  8.74k|          out.data() + (out.size() - label_size) + 4, label_size - 4);
 6440|  8.74k|      working.clear();
 6441|  8.74k|      if (!ada::idna::punycode_to_utf32(puny_segment_ascii, working)) {
  ------------------
  |  Branch (6441:11): [True: 384, False: 8.35k]
  ------------------
 6442|    384|        out.clear();
 6443|    384|        return false;
 6444|    384|      }
 6445|  8.35k|      if (is_ascii(working)) {
  ------------------
  |  Branch (6445:11): [True: 276, False: 8.08k]
  ------------------
 6446|    276|        out.clear();
 6447|    276|        return false;
 6448|    276|      }
 6449|  8.08k|      if (!ada::idna::map(working, post_map) || working != post_map) {
  ------------------
  |  Branch (6449:11): [True: 450, False: 7.63k]
  |  Branch (6449:49): [True: 273, False: 7.35k]
  ------------------
 6450|    723|        out.clear();
 6451|    723|        return false;
 6452|    723|      }
 6453|       |      // Mapping must be stable; NFC must not change the mapped form either.
 6454|  7.35k|      if (!is_ascii(post_map) && !is_already_nfc(post_map)) {
  ------------------
  |  Branch (6454:11): [True: 7.35k, False: 0]
  |  Branch (6454:34): [True: 4.75k, False: 2.60k]
  ------------------
 6455|  4.75k|        if (!normalize(post_map) || post_map != working) {
  ------------------
  |  Branch (6455:13): [True: 0, False: 4.75k]
  |  Branch (6455:37): [True: 327, False: 4.43k]
  ------------------
 6456|    327|          out.clear();
 6457|    327|          return false;
 6458|    327|        }
 6459|  4.75k|      }
 6460|  7.03k|      if (post_map.empty() || !is_label_valid(post_map)) {
  ------------------
  |  Branch (6460:11): [True: 0, False: 7.03k]
  |  Branch (6460:31): [True: 99, False: 6.93k]
  ------------------
 6461|     99|        out.clear();
 6462|     99|        return false;
 6463|     99|      }
 6464|  45.1k|    } else if (is_ascii(label_view)) {
  ------------------
  |  Branch (6464:16): [True: 5.63k, False: 39.5k]
  ------------------
 6465|  5.63k|      append_ascii_label(out, label_view);
 6466|  39.5k|    } else {
 6467|  39.5k|      if (!is_label_valid(label_view)) {
  ------------------
  |  Branch (6467:11): [True: 2.05k, False: 37.5k]
  ------------------
 6468|  2.05k|        out.clear();
 6469|  2.05k|        return false;
 6470|  2.05k|      }
 6471|  37.5k|      out.append("xn--");
 6472|  37.5k|      if (!ada::idna::utf32_to_punycode(label_view, out)) {
  ------------------
  |  Branch (6472:11): [True: 0, False: 37.5k]
  ------------------
 6473|      0|        out.clear();
 6474|      0|        return false;
 6475|      0|      }
 6476|  37.5k|    }
 6477|  53.2k|    if (!is_last_label) {
  ------------------
  |  Branch (6477:9): [True: 45.1k, False: 8.03k]
  ------------------
 6478|  45.1k|      out.push_back('.');
 6479|  45.1k|    }
 6480|  53.2k|  }
 6481|  8.55k|  return true;
 6482|  12.4k|}
_ZN3ada4idna8to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6484|  19.7k|std::string to_ascii(std::string_view ut8_string) {
 6485|  19.7k|  std::string out;
 6486|  19.7k|  if (!to_ascii(ut8_string, out)) {
  ------------------
  |  Branch (6486:7): [True: 5.10k, False: 14.6k]
  ------------------
 6487|  5.10k|    return {};
 6488|  5.10k|  }
 6489|  14.6k|  return out;
 6490|  19.7k|}
_ZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEE:
 7355|  2.11k|                    std::string& out) {
 7356|  2.11k|  ada_log("percent_encode ", input, " to output string while ",
 7357|  2.11k|          append ? "appending" : "overwriting");
 7358|  2.11k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7359|  2.11k|    return character_sets::bit_at(character_set, c);
 7360|  2.11k|  });
 7361|  2.11k|  ada_log("percent_encode done checking, moved to ",
 7362|  2.11k|          std::distance(input.begin(), pointer));
 7363|       |
 7364|       |  // Optimization: Don't iterate if percent encode is not required
 7365|  2.11k|  if (pointer == input.end()) {
  ------------------
  |  Branch (7365:7): [True: 1.38k, False: 729]
  ------------------
 7366|  1.38k|    ada_log("percent_encode encoding not needed.");
 7367|  1.38k|    return false;
 7368|  1.38k|  }
 7369|       |  if constexpr (!append) {
 7370|       |    out.clear();
 7371|       |  }
 7372|    729|  ada_log("percent_encode appending ", std::distance(input.begin(), pointer),
 7373|    729|          " bytes");
 7374|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7375|    729|  out.append(input.data(), std::distance(input.begin(), pointer));
 7376|    729|  ada_log("percent_encode processing ", std::distance(pointer, input.end()),
 7377|    729|          " bytes");
 7378|  18.1k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7378:10): [True: 17.4k, False: 729]
  ------------------
 7379|  17.4k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7379:9): [True: 15.1k, False: 2.30k]
  ------------------
 7380|  15.1k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7381|  15.1k|    } else {
 7382|  2.30k|      out += *pointer;
 7383|  2.30k|    }
 7384|  17.4k|  }
 7385|    729|  return true;
 7386|  2.11k|}
_ZN3ada7unicode14percent_encodeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEE:
 7355|  29.6k|                    std::string& out) {
 7356|  29.6k|  ada_log("percent_encode ", input, " to output string while ",
 7357|  29.6k|          append ? "appending" : "overwriting");
 7358|  29.6k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7359|  29.6k|    return character_sets::bit_at(character_set, c);
 7360|  29.6k|  });
 7361|  29.6k|  ada_log("percent_encode done checking, moved to ",
 7362|  29.6k|          std::distance(input.begin(), pointer));
 7363|       |
 7364|       |  // Optimization: Don't iterate if percent encode is not required
 7365|  29.6k|  if (pointer == input.end()) {
  ------------------
  |  Branch (7365:7): [True: 18.6k, False: 10.9k]
  ------------------
 7366|  18.6k|    ada_log("percent_encode encoding not needed.");
 7367|  18.6k|    return false;
 7368|  18.6k|  }
 7369|  10.9k|  if constexpr (!append) {
 7370|  10.9k|    out.clear();
 7371|  10.9k|  }
 7372|  10.9k|  ada_log("percent_encode appending ", std::distance(input.begin(), pointer),
 7373|  10.9k|          " bytes");
 7374|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7375|  10.9k|  out.append(input.data(), std::distance(input.begin(), pointer));
 7376|  10.9k|  ada_log("percent_encode processing ", std::distance(pointer, input.end()),
 7377|  10.9k|          " bytes");
 7378|   113k|  for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7378:10): [True: 102k, False: 10.9k]
  ------------------
 7379|   102k|    if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7379:9): [True: 98.5k, False: 3.99k]
  ------------------
 7380|  98.5k|      out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7381|  98.5k|    } else {
 7382|  3.99k|      out += *pointer;
 7383|  3.99k|    }
 7384|   102k|  }
 7385|  10.9k|  return true;
 7386|  29.6k|}
_ZN3ada7unicode14percent_decodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 7186|  4.23k|std::string percent_decode(const std::string_view input, size_t first_percent) {
 7187|       |  // next line is for safety only, we expect users to avoid calling
 7188|       |  // percent_decode when first_percent is outside the range.
 7189|  4.23k|  if (first_percent == std::string_view::npos) {
  ------------------
  |  Branch (7189:7): [True: 0, False: 4.23k]
  ------------------
 7190|      0|    return std::string(input);
 7191|      0|  }
 7192|       |
 7193|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7194|  4.23k|  const char* const src = input.data();
 7195|  4.23k|  const char* const end = src + input.size();
 7196|       |
 7197|       |  // Decoding never grows the string, so a single pre-sized buffer written via
 7198|       |  // bulk memcpy of the plain runs (then shrunk to the final length) avoids the
 7199|       |  // byte-at-a-time appends of the naive version.
 7200|  4.23k|  std::string out(input.size(), '\0');
 7201|  4.23k|  char* d = out.data();
 7202|  4.23k|  char* const d0 = d;
 7203|       |
 7204|  4.23k|  std::memcpy(d, src, first_percent);
 7205|  4.23k|  d += first_percent;
 7206|       |
 7207|  4.23k|  const char* p = src + first_percent;
 7208|  24.1k|  while (p < end) {
  ------------------
  |  Branch (7208:10): [True: 19.9k, False: 4.23k]
  ------------------
 7209|  19.9k|    if (*p == '%') {
  ------------------
  |  Branch (7209:9): [True: 13.9k, False: 5.97k]
  ------------------
 7210|       |      // Decode runs of valid %XX tightly (common for nested/encoded URLs).
 7211|  20.0k|      while (p + 2 < end && *p == '%') {
  ------------------
  |  Branch (7211:14): [True: 17.3k, False: 2.71k]
  |  Branch (7211:29): [True: 15.5k, False: 1.82k]
  ------------------
 7212|  15.5k|        if (!is_ascii_hex_digit(p[1]) || !is_ascii_hex_digit(p[2])) {
  ------------------
  |  Branch (7212:13): [True: 6.81k, False: 8.73k]
  |  Branch (7212:42): [True: 2.58k, False: 6.14k]
  ------------------
 7213|  9.40k|          break;
 7214|  9.40k|        }
 7215|  6.14k|        *d++ = static_cast<char>(convert_hex_to_binary(p[1]) * 16 +
 7216|  6.14k|                                 convert_hex_to_binary(p[2]));
 7217|  6.14k|        p += 3;
 7218|  6.14k|      }
 7219|  13.9k|      if (p < end && *p == '%') {
  ------------------
  |  Branch (7219:11): [True: 12.8k, False: 1.13k]
  |  Branch (7219:22): [True: 10.3k, False: 2.42k]
  ------------------
 7220|       |        // Not a valid escape (too few chars left or bad hex): copy '%'
 7221|       |        // literally and keep scanning after it.
 7222|  10.3k|        *d++ = *p++;
 7223|  10.3k|      }
 7224|  13.9k|    } else {
 7225|  5.97k|      const char* q = static_cast<const char*>(
 7226|  5.97k|          std::memchr(p, '%', static_cast<size_t>(end - p)));
 7227|  5.97k|      const char* run_end = q ? q : end;
  ------------------
  |  Branch (7227:29): [True: 3.49k, False: 2.47k]
  ------------------
 7228|  5.97k|      const size_t n = static_cast<size_t>(run_end - p);
 7229|  5.97k|      std::memcpy(d, p, n);
 7230|  5.97k|      d += n;
 7231|  5.97k|      p = run_end;
 7232|  5.97k|    }
 7233|  19.9k|  }
 7234|       |
 7235|  4.23k|  out.resize(static_cast<size_t>(d - d0));
 7236|  4.23k|  return out;
 7237|  4.23k|}
_ZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 7325|  24.9k|                           const uint8_t character_set[]) {
 7326|  24.9k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7327|  24.9k|    return character_sets::bit_at(character_set, c);
 7328|  24.9k|  });
 7329|       |  // Optimization: Don't iterate if percent encode is not required
 7330|  24.9k|  if (pointer == input.end()) {
  ------------------
  |  Branch (7330:7): [True: 20.9k, False: 3.99k]
  ------------------
 7331|  20.9k|    return std::string(input);
 7332|  20.9k|  }
 7333|       |
 7334|  3.99k|  std::string result;
 7335|  3.99k|  result.reserve(input.length());  // in the worst case, percent encoding might
 7336|       |                                   // produce 3 characters.
 7337|  3.99k|  result.append(input.substr(0, std::distance(input.begin(), pointer)));
 7338|  3.99k|  if (static_cast<size_t>(input.end() - pointer) >= 48) {
  ------------------
  |  Branch (7338:7): [True: 696, False: 3.29k]
  ------------------
 7339|    696|    percent_encode_suffix(&*pointer, input.data() + input.size(), character_set,
 7340|    696|                          result);
 7341|  3.29k|  } else {
 7342|  25.4k|    for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7342:12): [True: 22.2k, False: 3.29k]
  ------------------
 7343|  22.2k|      if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7343:11): [True: 19.4k, False: 2.72k]
  ------------------
 7344|  19.4k|        result.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7345|  19.4k|      } else {
 7346|  2.72k|        result += *pointer;
 7347|  2.72k|      }
 7348|  22.2k|    }
 7349|  3.29k|  }
 7350|  3.99k|  return result;
 7351|  24.9k|}
_ZN3ada7unicode8to_asciiERNSt3__18optionalINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEEENS1_17basic_string_viewIcS5_EEm:
 7396|  19.7k|              size_t first_percent) {
 7397|  19.7k|  std::string percent_decoded_buffer;
 7398|  19.7k|  std::string_view input = plain;
 7399|  19.7k|  if (first_percent != std::string_view::npos) {
  ------------------
  |  Branch (7399:7): [True: 1.20k, False: 18.5k]
  ------------------
 7400|  1.20k|    percent_decoded_buffer = unicode::percent_decode(plain, first_percent);
 7401|  1.20k|    input = percent_decoded_buffer;
 7402|  1.20k|  }
 7403|       |  // input is a non-empty UTF-8 string, must be percent decoded
 7404|  19.7k|  std::string idna_ascii = ada::idna::to_ascii(input);
 7405|  19.7k|  if (idna_ascii.empty() || contains_forbidden_domain_code_point(
  ------------------
  |  Branch (7405:7): [True: 5.15k, False: 14.6k]
  |  Branch (7405:29): [True: 1.25k, False: 13.3k]
  ------------------
 7406|  14.6k|                                idna_ascii.data(), idna_ascii.size())) {
 7407|  6.41k|    return false;
 7408|  6.41k|  }
 7409|  13.3k|  out = std::move(idna_ascii);
 7410|  13.3k|  return true;
 7411|  19.7k|}
_ZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKhm:
 7414|    524|                           const uint8_t character_set[], size_t index) {
 7415|    524|  std::string out;
 7416|       |  // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
 7417|    524|  out.append(input.data(), index);
 7418|    524|  auto pointer = input.begin() + index;
 7419|    524|  if (static_cast<size_t>(input.end() - pointer) >= 48) {
  ------------------
  |  Branch (7419:7): [True: 184, False: 340]
  ------------------
 7420|    184|    percent_encode_suffix(&*pointer, input.data() + input.size(), character_set,
 7421|    184|                          out);
 7422|    340|  } else {
 7423|  3.59k|    for (; pointer != input.end(); pointer++) {
  ------------------
  |  Branch (7423:12): [True: 3.25k, False: 340]
  ------------------
 7424|  3.25k|      if (character_sets::bit_at(character_set, *pointer)) {
  ------------------
  |  Branch (7424:11): [True: 2.73k, False: 520]
  ------------------
 7425|  2.73k|        out.append(character_sets::hex + uint8_t(*pointer) * 4, 3);
 7426|  2.73k|      } else {
 7427|    520|        out += *pointer;
 7428|    520|      }
 7429|  3.25k|    }
 7430|    340|  }
 7431|    524|  return out;
 7432|    524|}
_ZN3ada7unicode21percent_encode_suffixEPKcS2_PKhRNSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEE:
 7726|    880|                           const uint8_t character_set[], std::string& out) {
 7727|    880|#if ADA_UNICODE_SSSE3 || ADA_NEON || ADA_RVV
 7728|    880|  if (static_cast<size_t>(end - p) >= kPercentEncodeSimdMin) {
  ------------------
  |  Branch (7728:7): [True: 880, False: 0]
  ------------------
 7729|    880|    percent_encode_to_wide(p, end, character_set, out);
 7730|    880|    return;
 7731|    880|  }
 7732|      0|#endif
 7733|      0|  percent_encode_to_scalar(p, end, character_set, out);
 7734|      0|}
_ZN3ada11serializers36find_longest_sequence_of_ipv6_piecesERKNSt3__15arrayItLm8EEERmS6_:
 7799|    978|    size_t& compress_length) noexcept {
 7800|    978|  compress = 0;
 7801|    978|  compress_length = 0;
 7802|    978|  size_t i = 0;
 7803|  3.90k|  while (i < 8) {
  ------------------
  |  Branch (7803:10): [True: 2.92k, False: 978]
  ------------------
 7804|  2.92k|    if (address[i] != 0) {
  ------------------
  |  Branch (7804:9): [True: 1.83k, False: 1.09k]
  ------------------
 7805|  1.83k|      ++i;
 7806|  1.83k|      continue;
 7807|  1.83k|    }
 7808|  1.09k|    size_t next = i + 1;
 7809|  5.99k|    while (next < 8 && address[next] == 0) {
  ------------------
  |  Branch (7809:12): [True: 5.47k, False: 519]
  |  Branch (7809:24): [True: 4.89k, False: 576]
  ------------------
 7810|  4.89k|      ++next;
 7811|  4.89k|    }
 7812|  1.09k|    const size_t count = next - i;
 7813|  1.09k|    if (count > compress_length) {
  ------------------
  |  Branch (7813:9): [True: 993, False: 102]
  ------------------
 7814|    993|      compress_length = count;
 7815|    993|      compress = i;
 7816|    993|    }
 7817|  1.09k|    i = next;
 7818|  1.09k|  }
 7819|    978|}
_ZN3ada11serializers4ipv6ERKNSt3__15arrayItLm8EEE:
 7821|    978|std::string ipv6(const std::array<uint16_t, 8>& address) {
 7822|    978|  size_t compress = 0;
 7823|    978|  size_t compress_length = 0;
 7824|    978|  find_longest_sequence_of_ipv6_pieces(address, compress, compress_length);
 7825|       |
 7826|       |  // Skip compression when the longest zero run is a single piece.
 7827|    978|  if (compress_length <= 1) {
  ------------------
  |  Branch (7827:7): [True: 57, False: 921]
  ------------------
 7828|     57|    compress = compress_length = 8;
 7829|     57|  }
 7830|       |
 7831|       |  // Max: '[' + 8*4 hex + 7 ':' + ']' = 41
 7832|    978|  std::string output(4 * 8 + 7 + 2, '\0');
 7833|    978|  char* point = output.data();
 7834|    978|  *point++ = '[';
 7835|    978|  size_t piece_index = 0;
 7836|  2.49k|  while (true) {
  ------------------
  |  Branch (7836:10): [True: 2.49k, Folded]
  ------------------
 7837|  2.49k|    if (piece_index == compress) {
  ------------------
  |  Branch (7837:9): [True: 921, False: 1.57k]
  ------------------
 7838|    921|      *point++ = ':';
 7839|       |      // If we skip a value initially, we need to write '::'.
 7840|    921|      if (piece_index == 0) {
  ------------------
  |  Branch (7840:11): [True: 564, False: 357]
  ------------------
 7841|    564|        *point++ = ':';
 7842|    564|      }
 7843|    921|      piece_index += compress_length;
 7844|    921|      if (piece_index == 8) {
  ------------------
  |  Branch (7844:11): [True: 465, False: 456]
  ------------------
 7845|    465|        break;
 7846|    465|      }
 7847|    921|    }
 7848|  2.02k|    point = write_hex_u16(point, address[piece_index]);
 7849|  2.02k|    ++piece_index;
 7850|  2.02k|    if (piece_index == 8) {
  ------------------
  |  Branch (7850:9): [True: 513, False: 1.51k]
  ------------------
 7851|    513|      break;
 7852|    513|    }
 7853|  1.51k|    *point++ = ':';
 7854|  1.51k|  }
 7855|    978|  *point++ = ']';
 7856|    978|  output.resize(static_cast<size_t>(point - output.data()));
 7857|    978|  return output;
 7858|    978|}
_ZN3ada11serializers4ipv4Em:
 7860|  4.05k|std::string ipv4(const uint64_t address) {
 7861|  4.05k|  std::string output(15, '\0');
 7862|  4.05k|  char* point = output.data();
 7863|  4.05k|  point = write_u8(point, static_cast<uint8_t>(address >> 24));
 7864|  4.05k|  *point++ = '.';
 7865|  4.05k|  point = write_u8(point, static_cast<uint8_t>(address >> 16));
 7866|  4.05k|  *point++ = '.';
 7867|  4.05k|  point = write_u8(point, static_cast<uint8_t>(address >> 8));
 7868|  4.05k|  *point++ = '.';
 7869|  4.05k|  point = write_u8(point, static_cast<uint8_t>(address));
 7870|  4.05k|  output.resize(static_cast<size_t>(point - output.data()));
 7871|  4.05k|  return output;
 7872|  4.05k|}
_ZN3ada5parseINS_3urlEEEN2tl8expectedIT_NS_6errorsEEENSt3__117basic_string_viewIcNS7_11char_traitsIcEEEEPKS4_:
 8274|  46.5k|    std::string_view input, const result_type* base_url) {
 8275|  46.5k|  result_type u = ada::parser::parse_url_impl<result_type>(input, base_url);
 8276|  46.5k|  if (!u.is_valid) {
  ------------------
  |  Branch (8276:7): [True: 24.1k, False: 22.4k]
  ------------------
 8277|  24.1k|    return tl::unexpected(errors::type_error);
 8278|  24.1k|  }
 8279|  22.4k|  return u;
 8280|  46.5k|}
_ZN3ada5parseINS_14url_aggregatorEEEN2tl8expectedIT_NS_6errorsEEENSt3__117basic_string_viewIcNS7_11char_traitsIcEEEEPKS4_:
 8274|  46.5k|    std::string_view input, const result_type* base_url) {
 8275|  46.5k|  result_type u = ada::parser::parse_url_impl<result_type>(input, base_url);
 8276|  46.5k|  if (!u.is_valid) {
  ------------------
  |  Branch (8276:7): [True: 24.1k, False: 22.4k]
  ------------------
 8277|  24.1k|    return tl::unexpected(errors::type_error);
 8278|  24.1k|  }
 8279|  22.4k|  return u;
 8280|  46.5k|}
_ZN3ada20get_max_input_lengthEv:
 7895|   152k|uint32_t get_max_input_length() {
 7896|   152k|  return max_input_length_.load(std::memory_order_relaxed);
 7897|   152k|}
_ZN3ada9can_parseENSt3__117basic_string_viewIcNS0_11char_traitsIcEEEEPKS4_:
 8322|  42.6k|bool can_parse(std::string_view input, const std::string_view* base_input) {
 8323|       |  // Must match parse().has_value(), including post-normalization max length.
 8324|       |  // Percent-encoding expands a byte by at most 3x, but IDNA expands more: a
 8325|       |  // 3-byte UTF-8 label such as U+337F becomes the 17-byte "xn--6oqv20b1zgzxr",
 8326|       |  // so a dotted host sustains 4.5x. When the input (plus base, if any) fits in
 8327|       |  // max_length/5, the normalized href cannot exceed max_length, so
 8328|       |  // validation-only parsing (store_values=false) is safe.
 8329|       |
 8330|       |  // Hot path first: absolute special URLs, no base. Avoid loading max_length
 8331|       |  // until we need it (common absolute-fast true/false cases).
 8332|  42.6k|  if (base_input == nullptr) {
  ------------------
  |  Branch (8332:7): [True: 30.9k, False: 11.6k]
  ------------------
 8333|  30.9k|    auto r = try_can_parse_clean_http(input);
 8334|  30.9k|    if (!r.has_value()) {
  ------------------
  |  Branch (8334:9): [True: 30.8k, False: 178]
  ------------------
 8335|  30.8k|      r = try_can_parse_absolute_fast(input);
 8336|  30.8k|    }
 8337|  30.9k|    if (r.has_value()) {
  ------------------
  |  Branch (8337:9): [True: 17.1k, False: 13.8k]
  ------------------
 8338|  17.1k|      if (!*r) {
  ------------------
  |  Branch (8338:11): [True: 12.7k, False: 4.37k]
  ------------------
 8339|  12.7k|        return false;
 8340|  12.7k|      }
 8341|       |      // size <= max/5 => normalized href cannot exceed max (4.5x expansion).
 8342|       |      // Check this first: default max is ~4GB so almost all URLs return true.
 8343|  4.37k|      const uint32_t max_length = ada::get_max_input_length();
 8344|  4.37k|      if (input.size() <= static_cast<size_t>(max_length) / 5) {
  ------------------
  |  Branch (8344:11): [True: 4.37k, False: 0]
  ------------------
 8345|  4.37k|        return true;
 8346|  4.37k|      }
 8347|      0|      if (input.size() > max_length) {
  ------------------
  |  Branch (8347:11): [True: 0, False: 0]
  ------------------
 8348|      0|        return false;
 8349|      0|      }
 8350|      0|      return ada::parser::parse_url_impl<ada::url_aggregator, true>(input,
 8351|      0|                                                                    nullptr)
 8352|      0|          .is_valid;
 8353|      0|    }
 8354|  30.9k|  }
 8355|       |
 8356|  25.4k|  const uint32_t max_length = ada::get_max_input_length();
 8357|  25.4k|  if (input.size() > max_length) {
  ------------------
  |  Branch (8357:7): [True: 0, False: 25.4k]
  ------------------
 8358|      0|    return false;
 8359|      0|  }
 8360|  25.4k|  if (base_input != nullptr && base_input->size() > max_length) {
  ------------------
  |  Branch (8360:7): [True: 11.6k, False: 13.8k]
  |  Branch (8360:32): [True: 0, False: 11.6k]
  ------------------
 8361|      0|    return false;
 8362|      0|  }
 8363|       |
 8364|       |  // Relative resolution combines base + input; bound the sum so 4.5x expansion
 8365|       |  // of either side cannot push the final href past max_length.
 8366|  25.4k|  const size_t combined =
 8367|  25.4k|      input.size() + (base_input == nullptr ? 0 : base_input->size());
  ------------------
  |  Branch (8367:23): [True: 13.8k, False: 11.6k]
  ------------------
 8368|  25.4k|  const bool size_safe = combined <= static_cast<size_t>(max_length) / 5;
 8369|       |
 8370|  25.4k|  if (size_safe) {
  ------------------
  |  Branch (8370:7): [True: 25.4k, False: 0]
  ------------------
 8371|       |    // Validation-only: no buffer build, host still fully checked.
 8372|  25.4k|    ada::url_aggregator base_agg;
 8373|  25.4k|    ada::url_aggregator* base_ptr = nullptr;
 8374|  25.4k|    if (base_input != nullptr) {
  ------------------
  |  Branch (8374:9): [True: 11.6k, False: 13.8k]
  ------------------
 8375|  11.6k|      base_agg = ada::parser::parse_url_impl<ada::url_aggregator, false>(
 8376|  11.6k|          *base_input, nullptr);
 8377|  11.6k|      if (!base_agg.is_valid) {
  ------------------
  |  Branch (8377:11): [True: 7.69k, False: 3.93k]
  ------------------
 8378|  7.69k|        return false;
 8379|  7.69k|      }
 8380|  3.93k|      base_ptr = &base_agg;
 8381|  3.93k|    }
 8382|  17.7k|    return ada::parser::parse_url_impl<ada::url_aggregator, false>(input,
 8383|  17.7k|                                                                   base_ptr)
 8384|  17.7k|        .is_valid;
 8385|  25.4k|  }
 8386|       |
 8387|       |  // Near the limit: full parse so post-normalization length matches parse().
 8388|      0|  if (base_input == nullptr) {
  ------------------
  |  Branch (8388:7): [True: 0, False: 0]
  ------------------
 8389|      0|    return ada::parser::parse_url_impl<ada::url_aggregator, true>(input,
 8390|      0|                                                                  nullptr)
 8391|      0|        .is_valid;
 8392|      0|  }
 8393|      0|  ada::url_aggregator base_agg =
 8394|      0|      ada::parser::parse_url_impl<ada::url_aggregator, true>(*base_input,
 8395|      0|                                                             nullptr);
 8396|      0|  if (!base_agg.is_valid) {
  ------------------
  |  Branch (8396:7): [True: 0, False: 0]
  ------------------
 8397|      0|    return false;
 8398|      0|  }
 8399|      0|  return ada::parser::parse_url_impl<ada::url_aggregator, true>(input,
 8400|      0|                                                                &base_agg)
 8401|      0|      .is_valid;
 8402|      0|}
_ZN3ada7helpers18trim_c0_whitespaceERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9254|   113k|void trim_c0_whitespace(std::string_view& input) noexcept {
 9255|   115k|  while (!input.empty() &&
  ------------------
  |  Branch (9255:10): [True: 68.6k, False: 47.1k]
  ------------------
 9256|  68.6k|         ada::unicode::is_c0_control_or_space(input.front())) {
  ------------------
  |  Branch (9256:10): [True: 2.14k, False: 66.5k]
  ------------------
 9257|  2.14k|    input.remove_prefix(1);
 9258|  2.14k|  }
 9259|   116k|  while (!input.empty() && ada::unicode::is_c0_control_or_space(input.back())) {
  ------------------
  |  Branch (9259:10): [True: 69.1k, False: 47.1k]
  |  Branch (9259:28): [True: 2.57k, False: 66.5k]
  ------------------
 9260|  2.57k|    input.remove_suffix(1);
 9261|  2.57k|  }
 9262|   113k|}
_ZN3ada7helpers8overlapsENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERKNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 9429|     46|bool overlaps(std::string_view input1, const std::string& input2) noexcept {
 9430|     46|  ada_log("helpers::overlaps check if string_view '", input1, "' [",
 9431|     46|          input1.size(), " bytes] is part of string '", input2, "' [",
 9432|     46|          input2.size(), " bytes]");
 9433|     46|  return !input1.empty() && !input2.empty() && input1.data() >= input2.data() &&
  ------------------
  |  Branch (9433:10): [True: 46, False: 0]
  |  Branch (9433:29): [True: 46, False: 0]
  |  Branch (9433:48): [True: 16, False: 30]
  ------------------
 9434|     16|         input1.data() < input2.data() + input2.size();
  ------------------
  |  Branch (9434:10): [True: 0, False: 16]
  ------------------
 9435|     46|}
_ZN3ada3url17parse_opaque_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9810|    625|bool url::parse_opaque_host(std::string_view input) {
 9811|    625|  ada_log("parse_opaque_host ", input, " [", input.size(), " bytes]");
 9812|    625|  if (std::ranges::any_of(input, ada::unicode::is_forbidden_host_code_point)) {
  ------------------
  |  Branch (9812:7): [True: 10, False: 615]
  ------------------
 9813|     10|    return is_valid = false;
 9814|     10|  }
 9815|       |
 9816|       |  // Return the result of running UTF-8 percent-encode on input using the C0
 9817|       |  // control percent-encode set.
 9818|    615|  host = ada::unicode::percent_encode(
 9819|    615|      input, ada::character_sets::C0_CONTROL_PERCENT_ENCODE);
 9820|    615|  return true;
 9821|    625|}
_ZN3ada3url10parse_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9823|  1.79k|bool url::parse_ipv4(std::string_view input) {
 9824|  1.79k|  ada_log("parse_ipv4 ", input, " [", input.size(), " bytes]");
 9825|  1.79k|  if (input.empty()) {
  ------------------
  |  Branch (9825:7): [True: 0, False: 1.79k]
  ------------------
 9826|      0|    return is_valid = false;
 9827|      0|  }
 9828|  1.79k|  std::string_view original_input = input;
 9829|  1.79k|  if (original_input.back() == '.') {
  ------------------
  |  Branch (9829:7): [True: 72, False: 1.72k]
  ------------------
 9830|     72|    original_input.remove_suffix(1);
 9831|     72|  }
 9832|  1.79k|  if (input.back() == '.') {
  ------------------
  |  Branch (9832:7): [True: 72, False: 1.72k]
  ------------------
 9833|     72|    input.remove_suffix(1);
 9834|     72|    if (input.empty()) {
  ------------------
  |  Branch (9834:9): [True: 0, False: 72]
  ------------------
 9835|      0|      return is_valid = false;
 9836|      0|    }
 9837|     72|  }
 9838|       |
 9839|  1.79k|  const uint64_t fast = checkers::try_parse_ipv4_fast(input);
 9840|  1.79k|  if (fast < checkers::ipv4_fast_fail) [[likely]] {
  ------------------
  |  Branch (9840:7): [True: 26, False: 1.76k]
  ------------------
 9841|     26|    host = original_input;
 9842|     26|    host_type = IPV4;
 9843|     26|    return true;
 9844|     26|  }
 9845|       |
 9846|  1.76k|  const char* p = input.data();
 9847|  1.76k|  const char* end = p + input.size();
 9848|  1.76k|  uint64_t ipv4 = 0;
 9849|  1.76k|  int digit_count = 0;
 9850|  1.76k|  int pure_decimal_count = 0;
 9851|       |
 9852|  2.25k|  for (; digit_count < 4 && p < end; ++digit_count) {
  ------------------
  |  Branch (9852:10): [True: 2.24k, False: 12]
  |  Branch (9852:29): [True: 2.24k, False: 0]
  ------------------
 9853|  2.24k|    uint64_t segment = 0;
 9854|  2.24k|    bool pure = false;
 9855|  2.24k|    if (!detail::parse_ipv4_number(p, end, segment, pure)) {
  ------------------
  |  Branch (9855:9): [True: 343, False: 1.90k]
  ------------------
 9856|    343|      return is_valid = false;
 9857|    343|    }
 9858|  1.90k|    if (pure) {
  ------------------
  |  Branch (9858:9): [True: 1.36k, False: 535]
  ------------------
 9859|  1.36k|      ++pure_decimal_count;
 9860|  1.36k|    }
 9861|  1.90k|    if (p >= end) {
  ------------------
  |  Branch (9861:9): [True: 1.37k, False: 526]
  ------------------
 9862|  1.37k|      const unsigned shift = static_cast<unsigned>(32 - digit_count * 8);
 9863|  1.37k|      if (segment >= (uint64_t{1} << shift)) {
  ------------------
  |  Branch (9863:11): [True: 25, False: 1.35k]
  ------------------
 9864|     25|        return is_valid = false;
 9865|     25|      }
 9866|  1.35k|      ipv4 = (ipv4 << shift) | segment;
 9867|  1.35k|      host = (pure_decimal_count == 4) ? std::string(original_input)
  ------------------
  |  Branch (9867:14): [True: 0, False: 1.35k]
  ------------------
 9868|  1.35k|                                       : ada::serializers::ipv4(ipv4);
 9869|  1.35k|      host_type = IPV4;
 9870|  1.35k|      return true;
 9871|  1.37k|    }
 9872|    526|    if (segment > 255 || *p != '.') {
  ------------------
  |  Branch (9872:9): [True: 33, False: 493]
  |  Branch (9872:26): [True: 0, False: 493]
  ------------------
 9873|     33|      return is_valid = false;
 9874|     33|    }
 9875|    493|    ipv4 = (ipv4 << 8) | segment;
 9876|    493|    ++p;
 9877|    493|  }
 9878|     12|  if (digit_count != 4 || p != end) {
  ------------------
  |  Branch (9878:7): [True: 0, False: 12]
  |  Branch (9878:27): [True: 12, False: 0]
  ------------------
 9879|     12|    return is_valid = false;
 9880|     12|  }
 9881|      0|  host = (pure_decimal_count == 4) ? std::string(original_input)
  ------------------
  |  Branch (9881:10): [True: 0, False: 0]
  ------------------
 9882|      0|                                   : ada::serializers::ipv4(ipv4);
 9883|      0|  host_type = IPV4;
 9884|      0|  return true;
 9885|     12|}
_ZN3ada3url10parse_ipv6ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9887|    534|bool url::parse_ipv6(std::string_view input) {
 9888|    534|  ada_log("parse_ipv6 ", input, " [", input.size(), " bytes]");
 9889|    534|  if (input.empty() || input.size() > 45) [[unlikely]] {
  ------------------
  |  Branch (9889:7): [True: 16, False: 518]
  |  Branch (9889:24): [True: 13, False: 505]
  ------------------
 9890|     29|    return is_valid = false;
 9891|     29|  }
 9892|    505|  std::array<uint16_t, 8> address{};
 9893|       |#if defined(ADA_AVX512_IPV6)
 9894|       |  if (bool simd_valid; detail::try_parse_ipv6_avx512(input.data(), input.size(),
 9895|       |                                                     address, simd_valid)) {
 9896|       |    if (!simd_valid) [[unlikely]] {
 9897|       |      return is_valid = false;
 9898|       |    }
 9899|       |    host = ada::serializers::ipv6(address);
 9900|       |    host_type = IPV6;
 9901|       |    return true;
 9902|       |  }
 9903|       |  address = {};
 9904|       |#endif
 9905|    505|  const char* pointer = input.data();
 9906|    505|  const char* const end = pointer + input.size();
 9907|    505|  int piece_index = 0;
 9908|    505|  int compress = -1;
 9909|       |
 9910|    505|  if (*pointer == ':') {
  ------------------
  |  Branch (9910:7): [True: 197, False: 308]
  ------------------
 9911|    197|    if (input.size() == 1 || pointer[1] != ':') [[unlikely]] {
  ------------------
  |  Branch (9911:9): [True: 3, False: 194]
  |  Branch (9911:30): [True: 5, False: 189]
  ------------------
 9912|      8|      return is_valid = false;
 9913|      8|    }
 9914|    189|    pointer += 2;
 9915|    189|    compress = ++piece_index;
 9916|    189|  }
 9917|       |
 9918|  1.35k|  while (pointer != end) {
  ------------------
  |  Branch (9918:10): [True: 1.01k, False: 337]
  ------------------
 9919|  1.01k|    if (piece_index == 8) [[unlikely]] {
  ------------------
  |  Branch (9919:9): [True: 3, False: 1.01k]
  ------------------
 9920|      3|      return is_valid = false;
 9921|      3|    }
 9922|  1.01k|    if (*pointer == ':') {
  ------------------
  |  Branch (9922:9): [True: 137, False: 879]
  ------------------
 9923|    137|      if (compress != -1) [[unlikely]] {
  ------------------
  |  Branch (9923:11): [True: 3, False: 134]
  ------------------
 9924|      3|        return is_valid = false;
 9925|      3|      }
 9926|    134|      ++pointer;
 9927|    134|      compress = ++piece_index;
 9928|    134|      continue;
 9929|    137|    }
 9930|       |
 9931|    879|    uint16_t value = 0;
 9932|    879|    const int length = detail::parse_hex_piece(pointer, end, value);
 9933|       |
 9934|    879|    if (pointer != end && *pointer == '.') {
  ------------------
  |  Branch (9934:9): [True: 694, False: 185]
  |  Branch (9934:27): [True: 95, False: 599]
  ------------------
 9935|     95|      if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (9935:11): [True: 3, False: 92]
  ------------------
 9936|      3|        return is_valid = false;
 9937|      3|      }
 9938|     92|      pointer -= length;
 9939|     92|      if (piece_index > 6) [[unlikely]] {
  ------------------
  |  Branch (9939:11): [True: 3, False: 89]
  ------------------
 9940|      3|        return is_valid = false;
 9941|      3|      }
 9942|       |
 9943|     89|      int numbers_seen = 0;
 9944|    259|      while (pointer != end) {
  ------------------
  |  Branch (9944:14): [True: 229, False: 30]
  ------------------
 9945|    229|        int ipv4_piece = -1;
 9946|    229|        if (numbers_seen > 0) {
  ------------------
  |  Branch (9946:13): [True: 140, False: 89]
  ------------------
 9947|    140|          if (*pointer == '.' && numbers_seen < 4) {
  ------------------
  |  Branch (9947:15): [True: 123, False: 17]
  |  Branch (9947:34): [True: 118, False: 5]
  ------------------
 9948|    118|            ++pointer;
 9949|    118|          } else {
 9950|     22|            return is_valid = false;
 9951|     22|          }
 9952|    140|        }
 9953|    207|        if (pointer == end || *pointer < '0' || *pointer > '9') [[unlikely]] {
  ------------------
  |  Branch (9953:13): [True: 19, False: 188]
  |  Branch (9953:31): [True: 3, False: 185]
  |  Branch (9953:49): [True: 6, False: 179]
  ------------------
 9954|     28|          return is_valid = false;
 9955|     28|        }
 9956|    179|        ipv4_piece = *pointer - '0';
 9957|    179|        ++pointer;
 9958|    179|        if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (9958:13): [True: 161, False: 18]
  |  Branch (9958:31): [True: 64, False: 97]
  |  Branch (9958:50): [True: 59, False: 5]
  ------------------
 9959|     59|          if (ipv4_piece == 0) [[unlikely]] {
  ------------------
  |  Branch (9959:15): [True: 3, False: 56]
  ------------------
 9960|      3|            return is_valid = false;
 9961|      3|          }
 9962|     56|          ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
 9963|     56|          ++pointer;
 9964|     56|          if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (9964:15): [True: 50, False: 6]
  |  Branch (9964:33): [True: 31, False: 19]
  |  Branch (9964:52): [True: 27, False: 4]
  ------------------
 9965|     27|            ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
 9966|     27|            ++pointer;
 9967|     27|            if (ipv4_piece > 255) [[unlikely]] {
  ------------------
  |  Branch (9967:17): [True: 6, False: 21]
  ------------------
 9968|      6|              return is_valid = false;
 9969|      6|            }
 9970|     27|          }
 9971|     56|        }
 9972|    170|        address[static_cast<size_t>(piece_index)] = static_cast<uint16_t>(
 9973|    170|            address[static_cast<size_t>(piece_index)] * 0x100 +
 9974|    170|            static_cast<uint16_t>(ipv4_piece));
 9975|    170|        ++numbers_seen;
 9976|    170|        if (numbers_seen == 2 || numbers_seen == 4) {
  ------------------
  |  Branch (9976:13): [True: 53, False: 117]
  |  Branch (9976:34): [True: 17, False: 100]
  ------------------
 9977|     70|          ++piece_index;
 9978|     70|        }
 9979|    170|      }
 9980|     30|      if (numbers_seen != 4) [[unlikely]] {
  ------------------
  |  Branch (9980:11): [True: 18, False: 12]
  ------------------
 9981|     18|        return is_valid = false;
 9982|     18|      }
 9983|     12|      break;
 9984|     30|    }
 9985|       |
 9986|    784|    if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (9986:9): [True: 53, False: 731]
  ------------------
 9987|     53|      return is_valid = false;
 9988|     53|    }
 9989|       |
 9990|    731|    if (pointer != end && *pointer == ':') {
  ------------------
  |  Branch (9990:9): [True: 546, False: 185]
  |  Branch (9990:27): [True: 543, False: 3]
  ------------------
 9991|    543|      ++pointer;
 9992|    543|      if (pointer == end) [[unlikely]] {
  ------------------
  |  Branch (9992:11): [True: 3, False: 540]
  ------------------
 9993|      3|        return is_valid = false;
 9994|      3|      }
 9995|    543|    } else if (pointer != end) [[unlikely]] {
  ------------------
  |  Branch (9995:16): [True: 3, False: 185]
  ------------------
 9996|      3|      return is_valid = false;
 9997|      3|    }
 9998|       |
 9999|    725|    address[static_cast<size_t>(piece_index)] = value;
10000|    725|    ++piece_index;
10001|    725|  }
10002|       |
10003|    349|  if (compress != -1) {
  ------------------
  |  Branch (10003:7): [True: 314, False: 35]
  ------------------
10004|    314|    const int right = piece_index - compress;
10005|    314|    if (right > 0) {
  ------------------
  |  Branch (10005:9): [True: 162, False: 152]
  ------------------
10006|    162|      const size_t dest = static_cast<size_t>(8 - right);
10007|    162|      const size_t src = static_cast<size_t>(compress);
10008|    162|      if (dest != src) {
  ------------------
  |  Branch (10008:11): [True: 155, False: 7]
  ------------------
10009|    429|        for (size_t i = static_cast<size_t>(right); i-- > 0;) {
  ------------------
  |  Branch (10009:53): [True: 274, False: 155]
  ------------------
10010|    274|          address[dest + i] = address[src + i];
10011|    274|          address[src + i] = 0;
10012|    274|        }
10013|    155|      }
10014|    162|    }
10015|    314|  } else if (piece_index != 8) [[unlikely]] {
  ------------------
  |  Branch (10015:14): [True: 23, False: 12]
  ------------------
10016|     23|    return is_valid = false;
10017|     23|  }
10018|       |
10019|    326|  host = ada::serializers::ipv6(address);
10020|    326|  ada_log("parse_ipv6 ", *host);
10021|    326|  host_type = IPV6;
10022|    326|  return true;
10023|    349|}
_ZN3ada6parser25try_parse_simple_absoluteINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_:
11957|  41.9k|                                                result_type& out) {
11958|  41.9k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
11959|  41.9k|  constexpr bool is_aggregator =
11960|  41.9k|      std::is_same_v<result_type, ada::url_aggregator>;
11961|  41.9k|  static_assert(is_ada_url || is_aggregator);
11962|       |
11963|  41.9k|  const size_t len = input.size();
11964|       |  // Shortest accepted form is "ws://x" (6).
11965|  41.9k|  if (len < 6) [[unlikely]] {
  ------------------
  |  Branch (11965:7): [True: 23.5k, False: 18.4k]
  ------------------
11966|  23.5k|    return false;
11967|  23.5k|  }
11968|  18.4k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
11969|       |
11970|  18.4k|  size_t pos = 0;
11971|  18.4k|  ada::scheme::type scheme_type = ada::scheme::type::NOT_SPECIAL;
11972|  18.4k|  uint32_t protocol_end = 0;
11973|  18.4k|#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
11974|  18.4k|    defined(_M_X64) || defined(_M_IX86) || defined(_M_AMD64)
11975|       |  // One 8-byte load + integer compare is a single cmp/jcc on x86-64.
11976|       |  // Masked compares cover the shorter special schemes without extra loads.
11977|  18.4k|  if (len >= 8) {
  ------------------
  |  Branch (11977:7): [True: 15.7k, False: 2.68k]
  ------------------
11978|  15.7k|    uint64_t first8 = 0;
11979|  15.7k|    std::memcpy(&first8, b, 8);
11980|  15.7k|    if (first8 == 0x2f2f3a7370747468ull) {  // "https://"
  ------------------
  |  Branch (11980:9): [True: 11, False: 15.7k]
  ------------------
11981|     11|      pos = 8;
11982|     11|      scheme_type = ada::scheme::type::HTTPS;
11983|     11|      protocol_end = 6;
11984|  15.7k|    } else if ((first8 & 0x00ffffffffffffffull) ==
  ------------------
  |  Branch (11984:16): [True: 388, False: 15.3k]
  ------------------
11985|  15.7k|               0x002f2f3a70747468ull) {  // "http://"
11986|    388|      pos = 7;
11987|    388|      scheme_type = ada::scheme::type::HTTP;
11988|    388|      protocol_end = 5;
11989|  15.3k|    } else if ((first8 & 0x0000ffffffffffffull) ==
  ------------------
  |  Branch (11989:16): [True: 56, False: 15.2k]
  ------------------
11990|  15.3k|               0x00002f2f3a737377ull) {  // "wss://"
11991|     56|      pos = 6;
11992|     56|      scheme_type = ada::scheme::type::WSS;
11993|     56|      protocol_end = 4;
11994|  15.2k|    } else if ((first8 & 0x0000ffffffffffffull) ==
  ------------------
  |  Branch (11994:16): [True: 23, False: 15.2k]
  ------------------
11995|  15.2k|               0x00002f2f3a707466ull) {  // "ftp://"
11996|     23|      pos = 6;
11997|     23|      scheme_type = ada::scheme::type::FTP;
11998|     23|      protocol_end = 4;
11999|  15.2k|    } else if ((first8 & 0x000000ffffffffffull) ==
  ------------------
  |  Branch (11999:16): [True: 6.26k, False: 8.98k]
  ------------------
12000|  15.2k|               0x0000002f2f3a7377ull) {  // "ws://"
12001|  6.26k|      pos = 5;
12002|  6.26k|      scheme_type = ada::scheme::type::WS;
12003|  6.26k|      protocol_end = 3;
12004|  8.98k|    } else {
12005|  8.98k|      return false;
12006|  8.98k|    }
12007|  15.7k|  } else if (b[0] == 'w' && b[1] == 's') {
  ------------------
  |  Branch (12007:14): [True: 1.59k, False: 1.08k]
  |  Branch (12007:29): [True: 1.33k, False: 267]
  ------------------
12008|  1.33k|    if (b[2] == ':' && b[3] == '/' && b[4] == '/') {
  ------------------
  |  Branch (12008:9): [True: 1.23k, False: 100]
  |  Branch (12008:24): [True: 532, False: 700]
  |  Branch (12008:39): [True: 521, False: 11]
  ------------------
12009|    521|      pos = 5;
12010|    521|      scheme_type = ada::scheme::type::WS;
12011|    521|      protocol_end = 3;
12012|    811|    } else if (b[2] == 's' && b[3] == ':' && b[4] == '/' && b[5] == '/') {
  ------------------
  |  Branch (12012:16): [True: 74, False: 737]
  |  Branch (12012:31): [True: 64, False: 10]
  |  Branch (12012:46): [True: 10, False: 54]
  |  Branch (12012:61): [True: 4, False: 6]
  ------------------
12013|      4|      pos = 6;
12014|      4|      scheme_type = ada::scheme::type::WSS;
12015|      4|      protocol_end = 4;
12016|    807|    } else {
12017|    807|      return false;
12018|    807|    }
12019|  1.35k|  } else if (b[0] == 'f' && b[1] == 't' && b[2] == 'p' && b[3] == ':' &&
  ------------------
  |  Branch (12019:14): [True: 125, False: 1.22k]
  |  Branch (12019:29): [True: 48, False: 77]
  |  Branch (12019:44): [True: 36, False: 12]
  |  Branch (12019:59): [True: 23, False: 13]
  ------------------
12020|     23|             b[4] == '/' && b[5] == '/') {
  ------------------
  |  Branch (12020:14): [True: 11, False: 12]
  |  Branch (12020:29): [True: 3, False: 8]
  ------------------
12021|      3|    pos = 6;
12022|      3|    scheme_type = ada::scheme::type::FTP;
12023|      3|    protocol_end = 4;
12024|  1.35k|  } else if (len >= 7 && b[0] == 'h' && b[1] == 't' && b[2] == 't' &&
  ------------------
  |  Branch (12024:14): [True: 684, False: 666]
  |  Branch (12024:26): [True: 94, False: 590]
  |  Branch (12024:41): [True: 75, False: 19]
  |  Branch (12024:56): [True: 59, False: 16]
  ------------------
12025|     59|             b[3] == 'p' && b[4] == ':' && b[5] == '/' && b[6] == '/') {
  ------------------
  |  Branch (12025:14): [True: 52, False: 7]
  |  Branch (12025:29): [True: 37, False: 15]
  |  Branch (12025:44): [True: 16, False: 21]
  |  Branch (12025:59): [True: 3, False: 13]
  ------------------
12026|      3|    pos = 7;
12027|      3|    scheme_type = ada::scheme::type::HTTP;
12028|      3|    protocol_end = 5;
12029|  1.34k|  } else {
12030|  1.34k|    return false;
12031|  1.34k|  }
12032|       |#else
12033|       |  // Big-endian / uncommon arches: byte-wise special-scheme match.
12034|       |  if (len >= 8 && b[0] == 'h' && b[1] == 't' && b[2] == 't' && b[3] == 'p' &&
12035|       |      b[4] == 's' && b[5] == ':' && b[6] == '/' && b[7] == '/') {
12036|       |    pos = 8;
12037|       |    scheme_type = ada::scheme::type::HTTPS;
12038|       |    protocol_end = 6;
12039|       |  } else if (len >= 7 && b[0] == 'h' && b[1] == 't' && b[2] == 't' &&
12040|       |             b[3] == 'p' && b[4] == ':' && b[5] == '/' && b[6] == '/') {
12041|       |    pos = 7;
12042|       |    scheme_type = ada::scheme::type::HTTP;
12043|       |    protocol_end = 5;
12044|       |  } else if (b[0] == 'w' && b[1] == 's') {
12045|       |    if (b[2] == ':' && b[3] == '/' && b[4] == '/') {
12046|       |      pos = 5;
12047|       |      scheme_type = ada::scheme::type::WS;
12048|       |      protocol_end = 3;
12049|       |    } else if (len >= 6 && b[2] == 's' && b[3] == ':' && b[4] == '/' &&
12050|       |               b[5] == '/') {
12051|       |      pos = 6;
12052|       |      scheme_type = ada::scheme::type::WSS;
12053|       |      protocol_end = 4;
12054|       |    } else {
12055|       |      return false;
12056|       |    }
12057|       |  } else if (b[0] == 'f' && b[1] == 't' && b[2] == 'p' && b[3] == ':' &&
12058|       |             b[4] == '/' && b[5] == '/') {
12059|       |    pos = 6;
12060|       |    scheme_type = ada::scheme::type::FTP;
12061|       |    protocol_end = 4;
12062|       |  } else {
12063|       |    return false;
12064|       |  }
12065|       |#endif
12066|  7.27k|  if (pos < len && (b[pos] == '/' || b[pos] == '\\')) [[unlikely]] {
  ------------------
  |  Branch (12066:7): [True: 7.26k, False: 10]
  |  Branch (12066:21): [True: 12, False: 7.25k]
  |  Branch (12066:38): [True: 8, False: 7.24k]
  ------------------
12067|     20|    return false;
12068|     20|  }
12069|       |
12070|       |  // Digit-led hosts are IPv4/numeric; '[' is IPv6. Skip before scanning.
12071|  7.25k|  if (pos < len && ((b[pos] >= '0' && b[pos] <= '9') || b[pos] == '[')) {
  ------------------
  |  Branch (12071:7): [True: 7.24k, False: 10]
  |  Branch (12071:22): [True: 5.71k, False: 1.52k]
  |  Branch (12071:39): [True: 1.07k, False: 4.64k]
  |  Branch (12071:57): [True: 95, False: 6.07k]
  ------------------
12072|  1.16k|    return false;
12073|  1.16k|  }
12074|       |
12075|  6.08k|  const size_t host_start = pos;
12076|  6.08k|  bool has_upper = false;
12077|  6.08k|  bool has_x = false;
12078|  6.08k|  size_t host_end = pos;
12079|  6.08k|  if (!scan_plain_host(b, pos, len, host_end, has_upper, has_x)) {
  ------------------
  |  Branch (12079:7): [True: 255, False: 5.83k]
  ------------------
12080|    255|    return false;
12081|    255|  }
12082|  5.83k|  if (host_start == host_end) [[unlikely]] {
  ------------------
  |  Branch (12082:7): [True: 54, False: 5.77k]
  ------------------
12083|     54|    return false;
12084|     54|  }
12085|  5.77k|  const size_t host_len = host_end - host_start;
12086|  5.77k|  if (host_len > 253) [[unlikely]] {
  ------------------
  |  Branch (12086:7): [True: 98, False: 5.67k]
  ------------------
12087|     98|    return false;
12088|     98|  }
12089|  5.67k|  {
12090|  5.67k|    std::string_view hv(input.data() + host_start, host_len);
12091|  5.67k|    char host_buf[256];
12092|  5.67k|    if (has_upper) [[unlikely]] {
  ------------------
  |  Branch (12092:9): [True: 458, False: 5.22k]
  ------------------
12093|    458|      std::memcpy(host_buf, input.data() + host_start, host_len);
12094|    458|      unicode::to_lower_ascii(host_buf, host_len);
12095|    458|      hv = std::string_view(host_buf, host_len);
12096|    458|    }
12097|  5.67k|    if (ada::checkers::last_label_may_be_a_number(hv)) [[unlikely]] {
  ------------------
  |  Branch (12097:9): [True: 86, False: 5.59k]
  ------------------
12098|     86|      return false;
12099|     86|    }
12100|       |    // Punycode is "xn--...". The host scan already notes a lowercase 'x';
12101|       |    // uppercase 'X' is covered by has_upper after the in-place lower.
12102|  5.59k|    static constexpr std::string_view xn{"xn-", 3};
12103|  5.59k|    if ((has_x || has_upper) && hv.find(xn) != std::string_view::npos)
  ------------------
  |  Branch (12103:10): [True: 1.71k, False: 3.87k]
  |  Branch (12103:19): [True: 424, False: 3.45k]
  |  Branch (12103:33): [True: 1.24k, False: 895]
  ------------------
12104|  1.24k|        [[unlikely]] {
12105|  1.24k|      return false;
12106|  1.24k|    }
12107|  5.59k|  }
12108|       |
12109|  4.34k|  if (host_end < len && b[host_end] == ':') [[unlikely]] {
  ------------------
  |  Branch (12109:7): [True: 4.20k, False: 141]
  |  Branch (12109:25): [True: 1.21k, False: 2.98k]
  ------------------
12110|  1.21k|    return finish_simple_absolute_with_port(input, out, scheme_type,
12111|  1.21k|                                            protocol_end, host_start, host_end,
12112|  1.21k|                                            host_len, has_upper);
12113|  1.21k|  }
12114|       |
12115|  3.13k|  size_t i = host_end;
12116|  3.13k|  size_t path_start = host_end;
12117|  3.13k|  size_t path_end = host_end;
12118|  3.13k|  size_t query_start = std::string_view::npos;
12119|  3.13k|  size_t hash_start = std::string_view::npos;
12120|  3.13k|  bool has_path = false;
12121|  3.13k|  bool maybe_dot_segment = false;
12122|  3.13k|  bool rest_simple = true;
12123|       |
12124|  3.13k|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (12124:7): [True: 2.98k, False: 141]
  |  Branch (12124:18): [True: 2.86k, False: 122]
  ------------------
12125|  2.86k|    has_path = true;
12126|  2.86k|    path_start = i;
12127|  2.86k|    ++i;
12128|  2.86k|    scan_path_run(b, i, len, maybe_dot_segment);
12129|  2.86k|    if (i < len) {
  ------------------
  |  Branch (12129:9): [True: 1.12k, False: 1.74k]
  ------------------
12130|  1.12k|      const uint8_t cls = k_path[b[i]];
12131|  1.12k|      if (cls == 1) {
  ------------------
  |  Branch (12131:11): [True: 316, False: 811]
  ------------------
12132|    316|        path_end = i;
12133|    316|        if (b[i] == '?') {
  ------------------
  |  Branch (12133:13): [True: 196, False: 120]
  ------------------
12134|    196|          query_start = i;
12135|    196|          ++i;
12136|    196|          goto scan_query;
12137|    196|        }
12138|    120|        hash_start = i;
12139|    120|        ++i;
12140|    120|        goto scan_hash;
12141|    316|      }
12142|    811|      rest_simple = false;
12143|  34.0k|      for (; i < len; ++i) {
  ------------------
  |  Branch (12143:14): [True: 33.3k, False: 734]
  ------------------
12144|  33.3k|        if (b[i] == '?') {
  ------------------
  |  Branch (12144:13): [True: 56, False: 33.2k]
  ------------------
12145|     56|          path_end = i;
12146|     56|          query_start = i;
12147|     56|          ++i;
12148|     56|          goto scan_query_boundary;
12149|     56|        }
12150|  33.2k|        if (b[i] == '#') {
  ------------------
  |  Branch (12150:13): [True: 21, False: 33.2k]
  ------------------
12151|     21|          path_end = i;
12152|     21|          hash_start = i;
12153|     21|          ++i;
12154|     21|          goto after_rest;
12155|     21|        }
12156|  33.2k|      }
12157|    734|      path_end = i;
12158|    734|      goto after_rest;
12159|    811|    }
12160|  1.74k|    path_end = i;
12161|  1.74k|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12161:14): [True: 122, False: 141]
  |  Branch (12161:25): [True: 46, False: 76]
  ------------------
12162|     46|    query_start = i;
12163|     46|    ++i;
12164|     46|    goto scan_query;
12165|    217|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12165:14): [True: 76, False: 141]
  |  Branch (12165:25): [True: 76, False: 0]
  ------------------
12166|     76|    hash_start = i;
12167|     76|    ++i;
12168|     76|    goto scan_hash;
12169|     76|  }
12170|  1.88k|  goto after_rest;
12171|       |
12172|  1.88k|scan_query:
12173|    242|  scan_query_run(b, i, len);
12174|    242|  if (i < len) {
  ------------------
  |  Branch (12174:7): [True: 116, False: 126]
  ------------------
12175|    116|    if (b[i] == '#') {
  ------------------
  |  Branch (12175:9): [True: 83, False: 33]
  ------------------
12176|     83|      hash_start = i;
12177|     83|      ++i;
12178|     83|      goto scan_hash;
12179|     83|    }
12180|     33|    rest_simple = false;
12181|     33|    goto scan_query_boundary;
12182|    116|  }
12183|    126|  goto after_rest;
12184|       |
12185|    126|scan_query_boundary:
12186|    665|  for (; i < len; ++i) {
  ------------------
  |  Branch (12186:10): [True: 591, False: 74]
  ------------------
12187|    591|    if (b[i] == '#') {
  ------------------
  |  Branch (12187:9): [True: 15, False: 576]
  ------------------
12188|     15|      hash_start = i;
12189|     15|      ++i;
12190|     15|      goto after_rest;
12191|     15|    }
12192|    591|  }
12193|     74|  goto after_rest;
12194|       |
12195|    279|scan_hash:
12196|    279|  scan_hash_run(b, i, len);
12197|    279|  if (i < len) {
  ------------------
  |  Branch (12197:7): [True: 64, False: 215]
  ------------------
12198|     64|    rest_simple = false;
12199|     64|  }
12200|       |
12201|  3.13k|after_rest:
12202|  3.13k|  if (rest_simple && maybe_dot_segment) {
  ------------------
  |  Branch (12202:7): [True: 2.22k, False: 908]
  |  Branch (12202:22): [True: 382, False: 1.84k]
  ------------------
12203|    382|    const std::string_view path_body(input.data() + path_start,
12204|    382|                                     path_end - path_start);
12205|    382|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12205:9): [True: 188, False: 194]
  ------------------
12206|    188|      rest_simple = false;
12207|    188|    }
12208|    382|  }
12209|       |
12210|       |  // The slow path removes ASCII tab/newline anywhere in the input and trims a
12211|       |  // trailing C0 control or space; this fast path does neither. A query or
12212|       |  // fragment reaching the helpers below would keep those bytes percent-encoded
12213|       |  // ("?a\nb" -> "?a%0Ab", "#f " -> "#f%20") instead of stripped, so hand such
12214|       |  // inputs back to the slow path.
12215|  3.13k|  if (!rest_simple && (unicode::is_c0_control_or_space(input.back()) ||
  ------------------
  |  Branch (12215:7): [True: 1.09k, False: 2.03k]
  |  Branch (12215:24): [True: 18, False: 1.07k]
  ------------------
12216|  1.07k|                       unicode::has_tabs_or_newline(input))) {
  ------------------
  |  Branch (12216:24): [True: 21, False: 1.05k]
  ------------------
12217|     39|    return false;
12218|     39|  }
12219|       |
12220|  3.09k|  out.type = scheme_type;
12221|  3.09k|  out.is_valid = true;
12222|  3.09k|  out.has_opaque_path = false;
12223|  3.09k|  out.host_type = DEFAULT;
12224|       |
12225|  3.09k|  if (!rest_simple) {
  ------------------
  |  Branch (12225:7): [True: 1.05k, False: 2.03k]
  ------------------
12226|       |    // Host is a plain domain. Finish path/query/hash with
12227|       |    // the regular helpers
12228|       |    // so percent-encoding and dot segments do not re-parse the authority.
12229|  1.05k|    const std::string_view path_view =
12230|  1.05k|        has_path
  ------------------
  |  Branch (12230:9): [True: 999, False: 58]
  ------------------
12231|  1.05k|            ? std::string_view(input.data() + path_start, path_end - path_start)
12232|  1.05k|            : std::string_view{};
12233|  1.05k|    auto apply_query_and_hash = [&]() {
12234|  1.05k|      if (query_start != std::string_view::npos) {
12235|  1.05k|        const size_t q_end =
12236|  1.05k|            (hash_start != std::string_view::npos) ? hash_start : len;
12237|  1.05k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12238|  1.05k|                                                q_end - query_start - 1),
12239|  1.05k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12240|  1.05k|      }
12241|  1.05k|      if (hash_start != std::string_view::npos) {
12242|  1.05k|        out.update_unencoded_base_hash(std::string_view(
12243|  1.05k|            input.data() + hash_start + 1, len - hash_start - 1));
12244|  1.05k|      }
12245|  1.05k|    };
12246|       |    if constexpr (is_aggregator) {
12247|       |      out.buffer.assign(input.substr(0, host_end));
12248|       |      if (has_upper) {
12249|       |        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12250|       |      }
12251|       |      out.components.protocol_end = protocol_end;
12252|       |      out.components.username_end = protocol_end + 2;
12253|       |      out.components.host_start = protocol_end + 2;
12254|       |      out.components.host_end = static_cast<uint32_t>(host_end);
12255|       |      out.components.port = url_components::omitted;
12256|       |      out.components.pathname_start = static_cast<uint32_t>(host_end);
12257|       |      out.components.search_start = url_components::omitted;
12258|       |      out.components.hash_start = url_components::omitted;
12259|       |      out.parse_path(path_view);
12260|       |      apply_query_and_hash();
12261|  1.05k|    } else {
12262|  1.05k|      std::string host_str(input.substr(host_start, host_len));
12263|  1.05k|      if (has_upper) {
  ------------------
  |  Branch (12263:11): [True: 123, False: 934]
  ------------------
12264|    123|        unicode::to_lower_ascii(host_str.data(), host_str.size());
12265|    123|      }
12266|  1.05k|      out.host = std::move(host_str);
12267|  1.05k|      out.parse_path(path_view);
12268|  1.05k|      apply_query_and_hash();
12269|  1.05k|    }
12270|  1.05k|    return true;
12271|  1.05k|  }
12272|       |
12273|  2.03k|  const bool need_slash = !has_path;
12274|       |  if constexpr (is_aggregator) {
12275|       |    if (!need_slash) {
12276|       |      // assign copies once. resize()+memcpy would value-init then overwrite.
12277|       |      out.buffer.assign(input);
12278|       |      if (has_upper) {
12279|       |        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12280|       |      }
12281|       |      out.components.protocol_end = protocol_end;
12282|       |      out.components.username_end = protocol_end + 2;
12283|       |      out.components.host_start = protocol_end + 2;
12284|       |      out.components.host_end = static_cast<uint32_t>(host_end);
12285|       |      out.components.port = url_components::omitted;
12286|       |      out.components.pathname_start = static_cast<uint32_t>(path_start);
12287|       |      out.components.search_start = (query_start != std::string_view::npos)
12288|       |                                        ? static_cast<uint32_t>(query_start)
12289|       |                                        : url_components::omitted;
12290|       |      out.components.hash_start = (hash_start != std::string_view::npos)
12291|       |                                      ? static_cast<uint32_t>(hash_start)
12292|       |                                      : url_components::omitted;
12293|       |    } else {
12294|       |      out.buffer.clear();
12295|       |      out.buffer.reserve(len + 1);
12296|       |      out.buffer.append(input.substr(0, host_end));
12297|       |      out.buffer.push_back('/');
12298|       |      if (host_end < len) {
12299|       |        out.buffer.append(input.substr(host_end));
12300|       |      }
12301|       |      if (has_upper) {
12302|       |        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12303|       |      }
12304|       |      out.components.protocol_end = protocol_end;
12305|       |      out.components.username_end = protocol_end + 2;
12306|       |      out.components.host_start = protocol_end + 2;
12307|       |      out.components.host_end = static_cast<uint32_t>(host_end);
12308|       |      out.components.port = url_components::omitted;
12309|       |      out.components.pathname_start = static_cast<uint32_t>(host_end);
12310|       |      out.components.search_start = (query_start != std::string_view::npos)
12311|       |                                        ? static_cast<uint32_t>(query_start + 1)
12312|       |                                        : url_components::omitted;
12313|       |      out.components.hash_start = (hash_start != std::string_view::npos)
12314|       |                                      ? static_cast<uint32_t>(hash_start + 1)
12315|       |                                      : url_components::omitted;
12316|       |    }
12317|  2.03k|  } else {
12318|  2.03k|    std::string host_str(input.substr(host_start, host_len));
12319|  2.03k|    if (has_upper) {
  ------------------
  |  Branch (12319:9): [True: 106, False: 1.92k]
  ------------------
12320|    106|      unicode::to_lower_ascii(host_str.data(), host_str.size());
12321|    106|    }
12322|  2.03k|    out.host = std::move(host_str);
12323|  2.03k|    if (need_slash) {
  ------------------
  |  Branch (12323:9): [True: 182, False: 1.85k]
  ------------------
12324|    182|      out.path = "/";
12325|  1.85k|    } else {
12326|  1.85k|      out.path.assign(input.data() + path_start, path_end - path_start);
12327|  1.85k|    }
12328|  2.03k|    if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12328:9): [True: 207, False: 1.82k]
  ------------------
12329|    207|      const size_t q_end =
12330|    207|          (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12330:11): [True: 82, False: 125]
  ------------------
12331|    207|      out.query.emplace(input.data() + query_start + 1,
12332|    207|                        q_end - query_start - 1);
12333|    207|    }
12334|  2.03k|    if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12334:9): [True: 215, False: 1.81k]
  ------------------
12335|    215|      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
12336|    215|    }
12337|  2.03k|  }
12338|  2.03k|  return true;
12339|  3.09k|}
_ZN3ada6parser25try_parse_simple_absoluteINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_:
11957|  41.9k|                                                result_type& out) {
11958|  41.9k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
11959|  41.9k|  constexpr bool is_aggregator =
11960|  41.9k|      std::is_same_v<result_type, ada::url_aggregator>;
11961|  41.9k|  static_assert(is_ada_url || is_aggregator);
11962|       |
11963|  41.9k|  const size_t len = input.size();
11964|       |  // Shortest accepted form is "ws://x" (6).
11965|  41.9k|  if (len < 6) [[unlikely]] {
  ------------------
  |  Branch (11965:7): [True: 23.5k, False: 18.4k]
  ------------------
11966|  23.5k|    return false;
11967|  23.5k|  }
11968|  18.4k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
11969|       |
11970|  18.4k|  size_t pos = 0;
11971|  18.4k|  ada::scheme::type scheme_type = ada::scheme::type::NOT_SPECIAL;
11972|  18.4k|  uint32_t protocol_end = 0;
11973|  18.4k|#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
11974|  18.4k|    defined(_M_X64) || defined(_M_IX86) || defined(_M_AMD64)
11975|       |  // One 8-byte load + integer compare is a single cmp/jcc on x86-64.
11976|       |  // Masked compares cover the shorter special schemes without extra loads.
11977|  18.4k|  if (len >= 8) {
  ------------------
  |  Branch (11977:7): [True: 15.7k, False: 2.68k]
  ------------------
11978|  15.7k|    uint64_t first8 = 0;
11979|  15.7k|    std::memcpy(&first8, b, 8);
11980|  15.7k|    if (first8 == 0x2f2f3a7370747468ull) {  // "https://"
  ------------------
  |  Branch (11980:9): [True: 11, False: 15.7k]
  ------------------
11981|     11|      pos = 8;
11982|     11|      scheme_type = ada::scheme::type::HTTPS;
11983|     11|      protocol_end = 6;
11984|  15.7k|    } else if ((first8 & 0x00ffffffffffffffull) ==
  ------------------
  |  Branch (11984:16): [True: 388, False: 15.3k]
  ------------------
11985|  15.7k|               0x002f2f3a70747468ull) {  // "http://"
11986|    388|      pos = 7;
11987|    388|      scheme_type = ada::scheme::type::HTTP;
11988|    388|      protocol_end = 5;
11989|  15.3k|    } else if ((first8 & 0x0000ffffffffffffull) ==
  ------------------
  |  Branch (11989:16): [True: 56, False: 15.2k]
  ------------------
11990|  15.3k|               0x00002f2f3a737377ull) {  // "wss://"
11991|     56|      pos = 6;
11992|     56|      scheme_type = ada::scheme::type::WSS;
11993|     56|      protocol_end = 4;
11994|  15.2k|    } else if ((first8 & 0x0000ffffffffffffull) ==
  ------------------
  |  Branch (11994:16): [True: 23, False: 15.2k]
  ------------------
11995|  15.2k|               0x00002f2f3a707466ull) {  // "ftp://"
11996|     23|      pos = 6;
11997|     23|      scheme_type = ada::scheme::type::FTP;
11998|     23|      protocol_end = 4;
11999|  15.2k|    } else if ((first8 & 0x000000ffffffffffull) ==
  ------------------
  |  Branch (11999:16): [True: 6.26k, False: 8.98k]
  ------------------
12000|  15.2k|               0x0000002f2f3a7377ull) {  // "ws://"
12001|  6.26k|      pos = 5;
12002|  6.26k|      scheme_type = ada::scheme::type::WS;
12003|  6.26k|      protocol_end = 3;
12004|  8.98k|    } else {
12005|  8.98k|      return false;
12006|  8.98k|    }
12007|  15.7k|  } else if (b[0] == 'w' && b[1] == 's') {
  ------------------
  |  Branch (12007:14): [True: 1.59k, False: 1.08k]
  |  Branch (12007:29): [True: 1.33k, False: 267]
  ------------------
12008|  1.33k|    if (b[2] == ':' && b[3] == '/' && b[4] == '/') {
  ------------------
  |  Branch (12008:9): [True: 1.23k, False: 100]
  |  Branch (12008:24): [True: 532, False: 700]
  |  Branch (12008:39): [True: 521, False: 11]
  ------------------
12009|    521|      pos = 5;
12010|    521|      scheme_type = ada::scheme::type::WS;
12011|    521|      protocol_end = 3;
12012|    811|    } else if (b[2] == 's' && b[3] == ':' && b[4] == '/' && b[5] == '/') {
  ------------------
  |  Branch (12012:16): [True: 74, False: 737]
  |  Branch (12012:31): [True: 64, False: 10]
  |  Branch (12012:46): [True: 10, False: 54]
  |  Branch (12012:61): [True: 4, False: 6]
  ------------------
12013|      4|      pos = 6;
12014|      4|      scheme_type = ada::scheme::type::WSS;
12015|      4|      protocol_end = 4;
12016|    807|    } else {
12017|    807|      return false;
12018|    807|    }
12019|  1.35k|  } else if (b[0] == 'f' && b[1] == 't' && b[2] == 'p' && b[3] == ':' &&
  ------------------
  |  Branch (12019:14): [True: 125, False: 1.22k]
  |  Branch (12019:29): [True: 48, False: 77]
  |  Branch (12019:44): [True: 36, False: 12]
  |  Branch (12019:59): [True: 23, False: 13]
  ------------------
12020|     23|             b[4] == '/' && b[5] == '/') {
  ------------------
  |  Branch (12020:14): [True: 11, False: 12]
  |  Branch (12020:29): [True: 3, False: 8]
  ------------------
12021|      3|    pos = 6;
12022|      3|    scheme_type = ada::scheme::type::FTP;
12023|      3|    protocol_end = 4;
12024|  1.35k|  } else if (len >= 7 && b[0] == 'h' && b[1] == 't' && b[2] == 't' &&
  ------------------
  |  Branch (12024:14): [True: 684, False: 666]
  |  Branch (12024:26): [True: 94, False: 590]
  |  Branch (12024:41): [True: 75, False: 19]
  |  Branch (12024:56): [True: 59, False: 16]
  ------------------
12025|     59|             b[3] == 'p' && b[4] == ':' && b[5] == '/' && b[6] == '/') {
  ------------------
  |  Branch (12025:14): [True: 52, False: 7]
  |  Branch (12025:29): [True: 37, False: 15]
  |  Branch (12025:44): [True: 16, False: 21]
  |  Branch (12025:59): [True: 3, False: 13]
  ------------------
12026|      3|    pos = 7;
12027|      3|    scheme_type = ada::scheme::type::HTTP;
12028|      3|    protocol_end = 5;
12029|  1.34k|  } else {
12030|  1.34k|    return false;
12031|  1.34k|  }
12032|       |#else
12033|       |  // Big-endian / uncommon arches: byte-wise special-scheme match.
12034|       |  if (len >= 8 && b[0] == 'h' && b[1] == 't' && b[2] == 't' && b[3] == 'p' &&
12035|       |      b[4] == 's' && b[5] == ':' && b[6] == '/' && b[7] == '/') {
12036|       |    pos = 8;
12037|       |    scheme_type = ada::scheme::type::HTTPS;
12038|       |    protocol_end = 6;
12039|       |  } else if (len >= 7 && b[0] == 'h' && b[1] == 't' && b[2] == 't' &&
12040|       |             b[3] == 'p' && b[4] == ':' && b[5] == '/' && b[6] == '/') {
12041|       |    pos = 7;
12042|       |    scheme_type = ada::scheme::type::HTTP;
12043|       |    protocol_end = 5;
12044|       |  } else if (b[0] == 'w' && b[1] == 's') {
12045|       |    if (b[2] == ':' && b[3] == '/' && b[4] == '/') {
12046|       |      pos = 5;
12047|       |      scheme_type = ada::scheme::type::WS;
12048|       |      protocol_end = 3;
12049|       |    } else if (len >= 6 && b[2] == 's' && b[3] == ':' && b[4] == '/' &&
12050|       |               b[5] == '/') {
12051|       |      pos = 6;
12052|       |      scheme_type = ada::scheme::type::WSS;
12053|       |      protocol_end = 4;
12054|       |    } else {
12055|       |      return false;
12056|       |    }
12057|       |  } else if (b[0] == 'f' && b[1] == 't' && b[2] == 'p' && b[3] == ':' &&
12058|       |             b[4] == '/' && b[5] == '/') {
12059|       |    pos = 6;
12060|       |    scheme_type = ada::scheme::type::FTP;
12061|       |    protocol_end = 4;
12062|       |  } else {
12063|       |    return false;
12064|       |  }
12065|       |#endif
12066|  7.27k|  if (pos < len && (b[pos] == '/' || b[pos] == '\\')) [[unlikely]] {
  ------------------
  |  Branch (12066:7): [True: 7.26k, False: 10]
  |  Branch (12066:21): [True: 12, False: 7.25k]
  |  Branch (12066:38): [True: 8, False: 7.24k]
  ------------------
12067|     20|    return false;
12068|     20|  }
12069|       |
12070|       |  // Digit-led hosts are IPv4/numeric; '[' is IPv6. Skip before scanning.
12071|  7.25k|  if (pos < len && ((b[pos] >= '0' && b[pos] <= '9') || b[pos] == '[')) {
  ------------------
  |  Branch (12071:7): [True: 7.24k, False: 10]
  |  Branch (12071:22): [True: 5.71k, False: 1.52k]
  |  Branch (12071:39): [True: 1.07k, False: 4.64k]
  |  Branch (12071:57): [True: 95, False: 6.07k]
  ------------------
12072|  1.16k|    return false;
12073|  1.16k|  }
12074|       |
12075|  6.08k|  const size_t host_start = pos;
12076|  6.08k|  bool has_upper = false;
12077|  6.08k|  bool has_x = false;
12078|  6.08k|  size_t host_end = pos;
12079|  6.08k|  if (!scan_plain_host(b, pos, len, host_end, has_upper, has_x)) {
  ------------------
  |  Branch (12079:7): [True: 255, False: 5.83k]
  ------------------
12080|    255|    return false;
12081|    255|  }
12082|  5.83k|  if (host_start == host_end) [[unlikely]] {
  ------------------
  |  Branch (12082:7): [True: 54, False: 5.77k]
  ------------------
12083|     54|    return false;
12084|     54|  }
12085|  5.77k|  const size_t host_len = host_end - host_start;
12086|  5.77k|  if (host_len > 253) [[unlikely]] {
  ------------------
  |  Branch (12086:7): [True: 98, False: 5.67k]
  ------------------
12087|     98|    return false;
12088|     98|  }
12089|  5.67k|  {
12090|  5.67k|    std::string_view hv(input.data() + host_start, host_len);
12091|  5.67k|    char host_buf[256];
12092|  5.67k|    if (has_upper) [[unlikely]] {
  ------------------
  |  Branch (12092:9): [True: 458, False: 5.22k]
  ------------------
12093|    458|      std::memcpy(host_buf, input.data() + host_start, host_len);
12094|    458|      unicode::to_lower_ascii(host_buf, host_len);
12095|    458|      hv = std::string_view(host_buf, host_len);
12096|    458|    }
12097|  5.67k|    if (ada::checkers::last_label_may_be_a_number(hv)) [[unlikely]] {
  ------------------
  |  Branch (12097:9): [True: 86, False: 5.59k]
  ------------------
12098|     86|      return false;
12099|     86|    }
12100|       |    // Punycode is "xn--...". The host scan already notes a lowercase 'x';
12101|       |    // uppercase 'X' is covered by has_upper after the in-place lower.
12102|  5.59k|    static constexpr std::string_view xn{"xn-", 3};
12103|  5.59k|    if ((has_x || has_upper) && hv.find(xn) != std::string_view::npos)
  ------------------
  |  Branch (12103:10): [True: 1.71k, False: 3.87k]
  |  Branch (12103:19): [True: 424, False: 3.45k]
  |  Branch (12103:33): [True: 1.24k, False: 895]
  ------------------
12104|  1.24k|        [[unlikely]] {
12105|  1.24k|      return false;
12106|  1.24k|    }
12107|  5.59k|  }
12108|       |
12109|  4.34k|  if (host_end < len && b[host_end] == ':') [[unlikely]] {
  ------------------
  |  Branch (12109:7): [True: 4.20k, False: 141]
  |  Branch (12109:25): [True: 1.21k, False: 2.98k]
  ------------------
12110|  1.21k|    return finish_simple_absolute_with_port(input, out, scheme_type,
12111|  1.21k|                                            protocol_end, host_start, host_end,
12112|  1.21k|                                            host_len, has_upper);
12113|  1.21k|  }
12114|       |
12115|  3.13k|  size_t i = host_end;
12116|  3.13k|  size_t path_start = host_end;
12117|  3.13k|  size_t path_end = host_end;
12118|  3.13k|  size_t query_start = std::string_view::npos;
12119|  3.13k|  size_t hash_start = std::string_view::npos;
12120|  3.13k|  bool has_path = false;
12121|  3.13k|  bool maybe_dot_segment = false;
12122|  3.13k|  bool rest_simple = true;
12123|       |
12124|  3.13k|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (12124:7): [True: 2.98k, False: 141]
  |  Branch (12124:18): [True: 2.86k, False: 122]
  ------------------
12125|  2.86k|    has_path = true;
12126|  2.86k|    path_start = i;
12127|  2.86k|    ++i;
12128|  2.86k|    scan_path_run(b, i, len, maybe_dot_segment);
12129|  2.86k|    if (i < len) {
  ------------------
  |  Branch (12129:9): [True: 1.12k, False: 1.74k]
  ------------------
12130|  1.12k|      const uint8_t cls = k_path[b[i]];
12131|  1.12k|      if (cls == 1) {
  ------------------
  |  Branch (12131:11): [True: 316, False: 811]
  ------------------
12132|    316|        path_end = i;
12133|    316|        if (b[i] == '?') {
  ------------------
  |  Branch (12133:13): [True: 196, False: 120]
  ------------------
12134|    196|          query_start = i;
12135|    196|          ++i;
12136|    196|          goto scan_query;
12137|    196|        }
12138|    120|        hash_start = i;
12139|    120|        ++i;
12140|    120|        goto scan_hash;
12141|    316|      }
12142|    811|      rest_simple = false;
12143|  34.0k|      for (; i < len; ++i) {
  ------------------
  |  Branch (12143:14): [True: 33.3k, False: 734]
  ------------------
12144|  33.3k|        if (b[i] == '?') {
  ------------------
  |  Branch (12144:13): [True: 56, False: 33.2k]
  ------------------
12145|     56|          path_end = i;
12146|     56|          query_start = i;
12147|     56|          ++i;
12148|     56|          goto scan_query_boundary;
12149|     56|        }
12150|  33.2k|        if (b[i] == '#') {
  ------------------
  |  Branch (12150:13): [True: 21, False: 33.2k]
  ------------------
12151|     21|          path_end = i;
12152|     21|          hash_start = i;
12153|     21|          ++i;
12154|     21|          goto after_rest;
12155|     21|        }
12156|  33.2k|      }
12157|    734|      path_end = i;
12158|    734|      goto after_rest;
12159|    811|    }
12160|  1.74k|    path_end = i;
12161|  1.74k|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12161:14): [True: 122, False: 141]
  |  Branch (12161:25): [True: 46, False: 76]
  ------------------
12162|     46|    query_start = i;
12163|     46|    ++i;
12164|     46|    goto scan_query;
12165|    217|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12165:14): [True: 76, False: 141]
  |  Branch (12165:25): [True: 76, False: 0]
  ------------------
12166|     76|    hash_start = i;
12167|     76|    ++i;
12168|     76|    goto scan_hash;
12169|     76|  }
12170|  1.88k|  goto after_rest;
12171|       |
12172|  1.88k|scan_query:
12173|    242|  scan_query_run(b, i, len);
12174|    242|  if (i < len) {
  ------------------
  |  Branch (12174:7): [True: 116, False: 126]
  ------------------
12175|    116|    if (b[i] == '#') {
  ------------------
  |  Branch (12175:9): [True: 83, False: 33]
  ------------------
12176|     83|      hash_start = i;
12177|     83|      ++i;
12178|     83|      goto scan_hash;
12179|     83|    }
12180|     33|    rest_simple = false;
12181|     33|    goto scan_query_boundary;
12182|    116|  }
12183|    126|  goto after_rest;
12184|       |
12185|    126|scan_query_boundary:
12186|    665|  for (; i < len; ++i) {
  ------------------
  |  Branch (12186:10): [True: 591, False: 74]
  ------------------
12187|    591|    if (b[i] == '#') {
  ------------------
  |  Branch (12187:9): [True: 15, False: 576]
  ------------------
12188|     15|      hash_start = i;
12189|     15|      ++i;
12190|     15|      goto after_rest;
12191|     15|    }
12192|    591|  }
12193|     74|  goto after_rest;
12194|       |
12195|    279|scan_hash:
12196|    279|  scan_hash_run(b, i, len);
12197|    279|  if (i < len) {
  ------------------
  |  Branch (12197:7): [True: 64, False: 215]
  ------------------
12198|     64|    rest_simple = false;
12199|     64|  }
12200|       |
12201|  3.13k|after_rest:
12202|  3.13k|  if (rest_simple && maybe_dot_segment) {
  ------------------
  |  Branch (12202:7): [True: 2.22k, False: 908]
  |  Branch (12202:22): [True: 382, False: 1.84k]
  ------------------
12203|    382|    const std::string_view path_body(input.data() + path_start,
12204|    382|                                     path_end - path_start);
12205|    382|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12205:9): [True: 188, False: 194]
  ------------------
12206|    188|      rest_simple = false;
12207|    188|    }
12208|    382|  }
12209|       |
12210|       |  // The slow path removes ASCII tab/newline anywhere in the input and trims a
12211|       |  // trailing C0 control or space; this fast path does neither. A query or
12212|       |  // fragment reaching the helpers below would keep those bytes percent-encoded
12213|       |  // ("?a\nb" -> "?a%0Ab", "#f " -> "#f%20") instead of stripped, so hand such
12214|       |  // inputs back to the slow path.
12215|  3.13k|  if (!rest_simple && (unicode::is_c0_control_or_space(input.back()) ||
  ------------------
  |  Branch (12215:7): [True: 1.09k, False: 2.03k]
  |  Branch (12215:24): [True: 18, False: 1.07k]
  ------------------
12216|  1.07k|                       unicode::has_tabs_or_newline(input))) {
  ------------------
  |  Branch (12216:24): [True: 21, False: 1.05k]
  ------------------
12217|     39|    return false;
12218|     39|  }
12219|       |
12220|  3.09k|  out.type = scheme_type;
12221|  3.09k|  out.is_valid = true;
12222|  3.09k|  out.has_opaque_path = false;
12223|  3.09k|  out.host_type = DEFAULT;
12224|       |
12225|  3.09k|  if (!rest_simple) {
  ------------------
  |  Branch (12225:7): [True: 1.05k, False: 2.03k]
  ------------------
12226|       |    // Host is a plain domain. Finish path/query/hash with
12227|       |    // the regular helpers
12228|       |    // so percent-encoding and dot segments do not re-parse the authority.
12229|  1.05k|    const std::string_view path_view =
12230|  1.05k|        has_path
  ------------------
  |  Branch (12230:9): [True: 999, False: 58]
  ------------------
12231|  1.05k|            ? std::string_view(input.data() + path_start, path_end - path_start)
12232|  1.05k|            : std::string_view{};
12233|  1.05k|    auto apply_query_and_hash = [&]() {
12234|  1.05k|      if (query_start != std::string_view::npos) {
12235|  1.05k|        const size_t q_end =
12236|  1.05k|            (hash_start != std::string_view::npos) ? hash_start : len;
12237|  1.05k|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12238|  1.05k|                                                q_end - query_start - 1),
12239|  1.05k|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12240|  1.05k|      }
12241|  1.05k|      if (hash_start != std::string_view::npos) {
12242|  1.05k|        out.update_unencoded_base_hash(std::string_view(
12243|  1.05k|            input.data() + hash_start + 1, len - hash_start - 1));
12244|  1.05k|      }
12245|  1.05k|    };
12246|  1.05k|    if constexpr (is_aggregator) {
12247|  1.05k|      out.buffer.assign(input.substr(0, host_end));
12248|  1.05k|      if (has_upper) {
  ------------------
  |  Branch (12248:11): [True: 123, False: 934]
  ------------------
12249|    123|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12250|    123|      }
12251|  1.05k|      out.components.protocol_end = protocol_end;
12252|  1.05k|      out.components.username_end = protocol_end + 2;
12253|  1.05k|      out.components.host_start = protocol_end + 2;
12254|  1.05k|      out.components.host_end = static_cast<uint32_t>(host_end);
12255|  1.05k|      out.components.port = url_components::omitted;
12256|  1.05k|      out.components.pathname_start = static_cast<uint32_t>(host_end);
12257|  1.05k|      out.components.search_start = url_components::omitted;
12258|  1.05k|      out.components.hash_start = url_components::omitted;
12259|  1.05k|      out.parse_path(path_view);
12260|  1.05k|      apply_query_and_hash();
12261|       |    } else {
12262|       |      std::string host_str(input.substr(host_start, host_len));
12263|       |      if (has_upper) {
12264|       |        unicode::to_lower_ascii(host_str.data(), host_str.size());
12265|       |      }
12266|       |      out.host = std::move(host_str);
12267|       |      out.parse_path(path_view);
12268|       |      apply_query_and_hash();
12269|       |    }
12270|  1.05k|    return true;
12271|  1.05k|  }
12272|       |
12273|  2.03k|  const bool need_slash = !has_path;
12274|  2.03k|  if constexpr (is_aggregator) {
12275|  2.03k|    if (!need_slash) {
  ------------------
  |  Branch (12275:9): [True: 1.85k, False: 182]
  ------------------
12276|       |      // assign copies once. resize()+memcpy would value-init then overwrite.
12277|  1.85k|      out.buffer.assign(input);
12278|  1.85k|      if (has_upper) {
  ------------------
  |  Branch (12278:11): [True: 24, False: 1.82k]
  ------------------
12279|     24|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12280|     24|      }
12281|  1.85k|      out.components.protocol_end = protocol_end;
12282|  1.85k|      out.components.username_end = protocol_end + 2;
12283|  1.85k|      out.components.host_start = protocol_end + 2;
12284|  1.85k|      out.components.host_end = static_cast<uint32_t>(host_end);
12285|  1.85k|      out.components.port = url_components::omitted;
12286|  1.85k|      out.components.pathname_start = static_cast<uint32_t>(path_start);
12287|  1.85k|      out.components.search_start = (query_start != std::string_view::npos)
  ------------------
  |  Branch (12287:37): [True: 184, False: 1.66k]
  ------------------
12288|  1.85k|                                        ? static_cast<uint32_t>(query_start)
12289|  1.85k|                                        : url_components::omitted;
12290|  1.85k|      out.components.hash_start = (hash_start != std::string_view::npos)
  ------------------
  |  Branch (12290:35): [True: 187, False: 1.66k]
  ------------------
12291|  1.85k|                                      ? static_cast<uint32_t>(hash_start)
12292|  1.85k|                                      : url_components::omitted;
12293|  1.85k|    } else {
12294|    182|      out.buffer.clear();
12295|    182|      out.buffer.reserve(len + 1);
12296|    182|      out.buffer.append(input.substr(0, host_end));
12297|    182|      out.buffer.push_back('/');
12298|    182|      if (host_end < len) {
  ------------------
  |  Branch (12298:11): [True: 41, False: 141]
  ------------------
12299|     41|        out.buffer.append(input.substr(host_end));
12300|     41|      }
12301|    182|      if (has_upper) {
  ------------------
  |  Branch (12301:11): [True: 82, False: 100]
  ------------------
12302|     82|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
12303|     82|      }
12304|    182|      out.components.protocol_end = protocol_end;
12305|    182|      out.components.username_end = protocol_end + 2;
12306|    182|      out.components.host_start = protocol_end + 2;
12307|    182|      out.components.host_end = static_cast<uint32_t>(host_end);
12308|    182|      out.components.port = url_components::omitted;
12309|    182|      out.components.pathname_start = static_cast<uint32_t>(host_end);
12310|    182|      out.components.search_start = (query_start != std::string_view::npos)
  ------------------
  |  Branch (12310:37): [True: 23, False: 159]
  ------------------
12311|    182|                                        ? static_cast<uint32_t>(query_start + 1)
12312|    182|                                        : url_components::omitted;
12313|    182|      out.components.hash_start = (hash_start != std::string_view::npos)
  ------------------
  |  Branch (12313:35): [True: 28, False: 154]
  ------------------
12314|    182|                                      ? static_cast<uint32_t>(hash_start + 1)
12315|    182|                                      : url_components::omitted;
12316|    182|    }
12317|       |  } else {
12318|       |    std::string host_str(input.substr(host_start, host_len));
12319|       |    if (has_upper) {
12320|       |      unicode::to_lower_ascii(host_str.data(), host_str.size());
12321|       |    }
12322|       |    out.host = std::move(host_str);
12323|       |    if (need_slash) {
12324|       |      out.path = "/";
12325|       |    } else {
12326|       |      out.path.assign(input.data() + path_start, path_end - path_start);
12327|       |    }
12328|       |    if (query_start != std::string_view::npos) {
12329|       |      const size_t q_end =
12330|       |          (hash_start != std::string_view::npos) ? hash_start : len;
12331|       |      out.query.emplace(input.data() + query_start + 1,
12332|       |                        q_end - query_start - 1);
12333|       |    }
12334|       |    if (hash_start != std::string_view::npos) {
12335|       |      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
12336|       |    }
12337|       |  }
12338|  2.03k|  return true;
12339|  3.09k|}
_ZN3ada6parser32finish_simple_absolute_with_portINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_NS_6scheme4typeEjmmmb:
11685|  1.21k|    bool has_upper) {
11686|  1.21k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
11687|  1.21k|  constexpr bool is_aggregator =
11688|  1.21k|      std::is_same_v<result_type, ada::url_aggregator>;
11689|  1.21k|  static_assert(is_ada_url || is_aggregator);
11690|       |
11691|  1.21k|  const size_t len = input.size();
11692|  1.21k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
11693|  1.21k|  size_t p = host_end + 1;
11694|  1.21k|  uint32_t port_value = 0;
11695|  1.21k|  bool any_digit = false;
11696|  2.76k|  while (p < len && b[p] >= '0' && b[p] <= '9') {
  ------------------
  |  Branch (11696:10): [True: 2.70k, False: 69]
  |  Branch (11696:21): [True: 1.62k, False: 1.07k]
  |  Branch (11696:36): [True: 1.56k, False: 65]
  ------------------
11697|  1.56k|    any_digit = true;
11698|  1.56k|    port_value = port_value * 10 + static_cast<uint32_t>(b[p] - '0');
11699|  1.56k|    if (port_value > 65535) [[unlikely]] {
  ------------------
  |  Branch (11699:9): [True: 12, False: 1.55k]
  ------------------
11700|     12|      return false;
11701|     12|    }
11702|  1.55k|    ++p;
11703|  1.55k|  }
11704|  1.20k|  if (p < len && b[p] != '/' && b[p] != '?' && b[p] != '#') [[unlikely]] {
  ------------------
  |  Branch (11704:7): [True: 1.13k, False: 69]
  |  Branch (11704:18): [True: 98, False: 1.04k]
  |  Branch (11704:33): [True: 60, False: 38]
  |  Branch (11704:48): [True: 38, False: 22]
  ------------------
11705|     38|    return false;
11706|     38|  }
11707|  1.16k|  uint32_t parsed_port = url_components::omitted;
11708|  1.16k|  const uint16_t default_port = ada::scheme::get_special_port(scheme_type);
11709|  1.16k|  if (any_digit && port_value != default_port) {
  ------------------
  |  Branch (11709:7): [True: 639, False: 530]
  |  Branch (11709:20): [True: 635, False: 4]
  ------------------
11710|    635|    parsed_port = port_value;
11711|    635|  }
11712|  1.16k|  const size_t authority_end = p;
11713|       |
11714|  1.16k|  size_t i = authority_end;
11715|  1.16k|  size_t path_start = host_end;
11716|  1.16k|  size_t path_end = host_end;
11717|  1.16k|  size_t query_start = std::string_view::npos;
11718|  1.16k|  size_t hash_start = std::string_view::npos;
11719|  1.16k|  bool has_path = false;
11720|  1.16k|  bool maybe_dot_segment = false;
11721|  1.16k|  bool rest_simple = true;
11722|       |
11723|  1.16k|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (11723:7): [True: 1.10k, False: 69]
  |  Branch (11723:18): [True: 1.04k, False: 60]
  ------------------
11724|  1.04k|    has_path = true;
11725|  1.04k|    path_start = i;
11726|  1.04k|    ++i;
11727|  1.04k|    scan_path_run(b, i, len, maybe_dot_segment);
11728|  1.04k|    if (i < len) {
  ------------------
  |  Branch (11728:9): [True: 639, False: 401]
  ------------------
11729|    639|      const uint8_t cls = k_path[b[i]];
11730|    639|      if (cls == 1) {
  ------------------
  |  Branch (11730:11): [True: 95, False: 544]
  ------------------
11731|     95|        path_end = i;
11732|     95|        if (b[i] == '?') {
  ------------------
  |  Branch (11732:13): [True: 59, False: 36]
  ------------------
11733|     59|          query_start = i;
11734|     59|          ++i;
11735|     59|          goto scan_query;
11736|     59|        }
11737|     36|        hash_start = i;
11738|     36|        ++i;
11739|     36|        goto scan_hash;
11740|     95|      }
11741|    544|      rest_simple = false;
11742|  23.2k|      for (; i < len; ++i) {
  ------------------
  |  Branch (11742:14): [True: 22.7k, False: 493]
  ------------------
11743|  22.7k|        if (b[i] == '?') {
  ------------------
  |  Branch (11743:13): [True: 38, False: 22.7k]
  ------------------
11744|     38|          path_end = i;
11745|     38|          query_start = i;
11746|     38|          ++i;
11747|     38|          goto scan_query_boundary;
11748|     38|        }
11749|  22.7k|        if (b[i] == '#') {
  ------------------
  |  Branch (11749:13): [True: 13, False: 22.7k]
  ------------------
11750|     13|          path_end = i;
11751|     13|          hash_start = i;
11752|     13|          ++i;
11753|     13|          goto after_rest;
11754|     13|        }
11755|  22.7k|      }
11756|    493|      path_end = i;
11757|    493|      goto after_rest;
11758|    544|    }
11759|    401|    path_end = i;
11760|    401|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (11760:14): [True: 60, False: 69]
  |  Branch (11760:25): [True: 38, False: 22]
  ------------------
11761|     38|    query_start = i;
11762|     38|    ++i;
11763|     38|    goto scan_query;
11764|     91|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (11764:14): [True: 22, False: 69]
  |  Branch (11764:25): [True: 22, False: 0]
  ------------------
11765|     22|    hash_start = i;
11766|     22|    ++i;
11767|     22|    goto scan_hash;
11768|     22|  }
11769|    470|  goto after_rest;
11770|       |
11771|    470|scan_query:
11772|     97|  scan_query_run(b, i, len);
11773|     97|  if (i < len) {
  ------------------
  |  Branch (11773:7): [True: 43, False: 54]
  ------------------
11774|     43|    if (b[i] == '#') {
  ------------------
  |  Branch (11774:9): [True: 20, False: 23]
  ------------------
11775|     20|      hash_start = i;
11776|     20|      ++i;
11777|     20|      goto scan_hash;
11778|     20|    }
11779|     23|    rest_simple = false;
11780|     23|    goto scan_query_boundary;
11781|     43|  }
11782|     54|  goto after_rest;
11783|       |
11784|     61|scan_query_boundary:
11785|    708|  for (; i < len; ++i) {
  ------------------
  |  Branch (11785:10): [True: 661, False: 47]
  ------------------
11786|    661|    if (b[i] == '#') {
  ------------------
  |  Branch (11786:9): [True: 14, False: 647]
  ------------------
11787|     14|      hash_start = i;
11788|     14|      ++i;
11789|     14|      goto after_rest;
11790|     14|    }
11791|    661|  }
11792|     47|  goto after_rest;
11793|       |
11794|     78|scan_hash:
11795|     78|  scan_hash_run(b, i, len);
11796|     78|  if (i < len) {
  ------------------
  |  Branch (11796:7): [True: 13, False: 65]
  ------------------
11797|     13|    rest_simple = false;
11798|     13|  }
11799|       |
11800|  1.16k|after_rest:
11801|  1.16k|  if (rest_simple && maybe_dot_segment) {
  ------------------
  |  Branch (11801:7): [True: 589, False: 580]
  |  Branch (11801:22): [True: 256, False: 333]
  ------------------
11802|    256|    const std::string_view path_body(input.data() + path_start,
11803|    256|                                     path_end - path_start);
11804|    256|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (11804:9): [True: 247, False: 9]
  ------------------
11805|    247|      rest_simple = false;
11806|    247|    }
11807|    256|  }
11808|       |
11809|       |  // The slow path removes ASCII tab/newline anywhere in the input and trims a
11810|       |  // trailing C0 control or space; this fast path does neither. A query or
11811|       |  // fragment reaching the helpers below would keep those bytes percent-encoded
11812|       |  // ("?a\nb" -> "?a%0Ab", "#f " -> "#f%20") instead of stripped, so hand such
11813|       |  // inputs back to the slow path.
11814|  1.16k|  if (!rest_simple && (unicode::is_c0_control_or_space(input.back()) ||
  ------------------
  |  Branch (11814:7): [True: 827, False: 342]
  |  Branch (11814:24): [True: 7, False: 820]
  ------------------
11815|    820|                       unicode::has_tabs_or_newline(input))) {
  ------------------
  |  Branch (11815:24): [True: 20, False: 800]
  ------------------
11816|     27|    return false;
11817|     27|  }
11818|       |
11819|  1.14k|  out.type = scheme_type;
11820|  1.14k|  out.is_valid = true;
11821|  1.14k|  out.has_opaque_path = false;
11822|  1.14k|  out.host_type = DEFAULT;
11823|       |
11824|  1.14k|  const uint32_t port_bytes = (parsed_port != url_components::omitted)
  ------------------
  |  Branch (11824:31): [True: 634, False: 508]
  ------------------
11825|  1.14k|                                  ? (1 + port_decimal_digit_count(parsed_port))
11826|  1.14k|                                  : 0;
11827|       |
11828|  1.14k|  if (!rest_simple) {
  ------------------
  |  Branch (11828:7): [True: 800, False: 342]
  ------------------
11829|    800|    const std::string_view path_view =
11830|    800|        has_path
  ------------------
  |  Branch (11830:9): [True: 785, False: 15]
  ------------------
11831|    800|            ? std::string_view(input.data() + path_start, path_end - path_start)
11832|    800|            : std::string_view{};
11833|    800|    auto apply_query_and_hash = [&]() {
11834|    800|      if (query_start != std::string_view::npos) {
11835|    800|        const size_t q_end =
11836|    800|            (hash_start != std::string_view::npos) ? hash_start : len;
11837|    800|        out.update_base_search(std::string_view(input.data() + query_start + 1,
11838|    800|                                                q_end - query_start - 1),
11839|    800|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
11840|    800|      }
11841|    800|      if (hash_start != std::string_view::npos) {
11842|    800|        out.update_unencoded_base_hash(std::string_view(
11843|    800|            input.data() + hash_start + 1, len - hash_start - 1));
11844|    800|      }
11845|    800|    };
11846|       |    if constexpr (is_aggregator) {
11847|       |      out.buffer.assign(input.substr(0, host_end));
11848|       |      if (parsed_port != url_components::omitted) {
11849|       |        append_canonical_port(out.buffer, parsed_port);
11850|       |      }
11851|       |      if (has_upper) {
11852|       |        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
11853|       |      }
11854|       |      out.components.protocol_end = protocol_end;
11855|       |      out.components.username_end = protocol_end + 2;
11856|       |      out.components.host_start = protocol_end + 2;
11857|       |      out.components.host_end = static_cast<uint32_t>(host_end);
11858|       |      out.components.port = parsed_port;
11859|       |      out.components.pathname_start = static_cast<uint32_t>(out.buffer.size());
11860|       |      out.components.search_start = url_components::omitted;
11861|       |      out.components.hash_start = url_components::omitted;
11862|       |      out.parse_path(path_view);
11863|       |      apply_query_and_hash();
11864|    800|    } else {
11865|    800|      std::string host_str(input.substr(host_start, host_len));
11866|    800|      if (has_upper) {
  ------------------
  |  Branch (11866:11): [True: 122, False: 678]
  ------------------
11867|    122|        unicode::to_lower_ascii(host_str.data(), host_str.size());
11868|    122|      }
11869|    800|      out.host = std::move(host_str);
11870|    800|      if (parsed_port != url_components::omitted) {
  ------------------
  |  Branch (11870:11): [True: 352, False: 448]
  ------------------
11871|    352|        out.port = static_cast<uint16_t>(parsed_port);
11872|    352|      }
11873|    800|      out.parse_path(path_view);
11874|    800|      apply_query_and_hash();
11875|    800|    }
11876|    800|    return true;
11877|    800|  }
11878|       |
11879|    342|  const bool insert_slash = !has_path;
11880|       |  if constexpr (is_aggregator) {
11881|       |    const size_t out_len =
11882|       |        host_end + port_bytes + (insert_slash ? 1 : 0) + (len - authority_end);
11883|       |    const bool omit_port_bytes = parsed_port == url_components::omitted;
11884|       |    if (out_len == len && !insert_slash && !omit_port_bytes) {
11885|       |      out.buffer.assign(input);
11886|       |    } else {
11887|       |      out.buffer.clear();
11888|       |      out.buffer.reserve(out_len);
11889|       |      out.buffer.append(input.substr(0, host_end));
11890|       |      if (parsed_port != url_components::omitted) {
11891|       |        append_canonical_port(out.buffer, parsed_port);
11892|       |      }
11893|       |      if (insert_slash) {
11894|       |        out.buffer.push_back('/');
11895|       |      }
11896|       |      if (authority_end < len) {
11897|       |        out.buffer.append(input.substr(authority_end));
11898|       |      }
11899|       |    }
11900|       |    if (has_upper) {
11901|       |      unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
11902|       |    }
11903|       |    const uint32_t pathname_start =
11904|       |        static_cast<uint32_t>(host_end) + port_bytes;
11905|       |    const int32_t tail_delta = static_cast<int32_t>(pathname_start) +
11906|       |                               (insert_slash ? 1 : 0) -
11907|       |                               static_cast<int32_t>(authority_end);
11908|       |    out.components.protocol_end = protocol_end;
11909|       |    out.components.username_end = protocol_end + 2;
11910|       |    out.components.host_start = protocol_end + 2;
11911|       |    out.components.host_end = static_cast<uint32_t>(host_end);
11912|       |    out.components.port = parsed_port;
11913|       |    out.components.pathname_start = pathname_start;
11914|       |    out.components.search_start =
11915|       |        (query_start != std::string_view::npos)
11916|       |            ? static_cast<uint32_t>(static_cast<int32_t>(query_start) +
11917|       |                                    tail_delta)
11918|       |            : url_components::omitted;
11919|       |    out.components.hash_start =
11920|       |        (hash_start != std::string_view::npos)
11921|       |            ? static_cast<uint32_t>(static_cast<int32_t>(hash_start) +
11922|       |                                    tail_delta)
11923|       |            : url_components::omitted;
11924|    342|  } else {
11925|    342|    std::string host_str(input.substr(host_start, host_len));
11926|    342|    if (has_upper) {
  ------------------
  |  Branch (11926:9): [True: 64, False: 278]
  ------------------
11927|     64|      unicode::to_lower_ascii(host_str.data(), host_str.size());
11928|     64|    }
11929|    342|    out.host = std::move(host_str);
11930|    342|    if (parsed_port != url_components::omitted) {
  ------------------
  |  Branch (11930:9): [True: 282, False: 60]
  ------------------
11931|    282|      out.port = static_cast<uint16_t>(parsed_port);
11932|    282|    }
11933|    342|    if (insert_slash) {
  ------------------
  |  Branch (11933:9): [True: 108, False: 234]
  ------------------
11934|    108|      out.path = "/";
11935|    234|    } else {
11936|    234|      out.path.assign(input.data() + path_start, path_end - path_start);
11937|    234|    }
11938|    342|    if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (11938:9): [True: 72, False: 270]
  ------------------
11939|     72|      const size_t q_end =
11940|     72|          (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (11940:11): [True: 19, False: 53]
  ------------------
11941|     72|      out.query.emplace(input.data() + query_start + 1,
11942|     72|                        q_end - query_start - 1);
11943|     72|    }
11944|    342|    if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (11944:9): [True: 65, False: 277]
  ------------------
11945|     65|      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
11946|     65|    }
11947|    342|  }
11948|    342|  return true;
11949|  1.14k|}
_ZN3ada6parser32finish_simple_absolute_with_portINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_NS_6scheme4typeEjmmmb:
11685|  1.21k|    bool has_upper) {
11686|  1.21k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
11687|  1.21k|  constexpr bool is_aggregator =
11688|  1.21k|      std::is_same_v<result_type, ada::url_aggregator>;
11689|  1.21k|  static_assert(is_ada_url || is_aggregator);
11690|       |
11691|  1.21k|  const size_t len = input.size();
11692|  1.21k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
11693|  1.21k|  size_t p = host_end + 1;
11694|  1.21k|  uint32_t port_value = 0;
11695|  1.21k|  bool any_digit = false;
11696|  2.76k|  while (p < len && b[p] >= '0' && b[p] <= '9') {
  ------------------
  |  Branch (11696:10): [True: 2.70k, False: 69]
  |  Branch (11696:21): [True: 1.62k, False: 1.07k]
  |  Branch (11696:36): [True: 1.56k, False: 65]
  ------------------
11697|  1.56k|    any_digit = true;
11698|  1.56k|    port_value = port_value * 10 + static_cast<uint32_t>(b[p] - '0');
11699|  1.56k|    if (port_value > 65535) [[unlikely]] {
  ------------------
  |  Branch (11699:9): [True: 12, False: 1.55k]
  ------------------
11700|     12|      return false;
11701|     12|    }
11702|  1.55k|    ++p;
11703|  1.55k|  }
11704|  1.20k|  if (p < len && b[p] != '/' && b[p] != '?' && b[p] != '#') [[unlikely]] {
  ------------------
  |  Branch (11704:7): [True: 1.13k, False: 69]
  |  Branch (11704:18): [True: 98, False: 1.04k]
  |  Branch (11704:33): [True: 60, False: 38]
  |  Branch (11704:48): [True: 38, False: 22]
  ------------------
11705|     38|    return false;
11706|     38|  }
11707|  1.16k|  uint32_t parsed_port = url_components::omitted;
11708|  1.16k|  const uint16_t default_port = ada::scheme::get_special_port(scheme_type);
11709|  1.16k|  if (any_digit && port_value != default_port) {
  ------------------
  |  Branch (11709:7): [True: 639, False: 530]
  |  Branch (11709:20): [True: 635, False: 4]
  ------------------
11710|    635|    parsed_port = port_value;
11711|    635|  }
11712|  1.16k|  const size_t authority_end = p;
11713|       |
11714|  1.16k|  size_t i = authority_end;
11715|  1.16k|  size_t path_start = host_end;
11716|  1.16k|  size_t path_end = host_end;
11717|  1.16k|  size_t query_start = std::string_view::npos;
11718|  1.16k|  size_t hash_start = std::string_view::npos;
11719|  1.16k|  bool has_path = false;
11720|  1.16k|  bool maybe_dot_segment = false;
11721|  1.16k|  bool rest_simple = true;
11722|       |
11723|  1.16k|  if (i < len && b[i] == '/') {
  ------------------
  |  Branch (11723:7): [True: 1.10k, False: 69]
  |  Branch (11723:18): [True: 1.04k, False: 60]
  ------------------
11724|  1.04k|    has_path = true;
11725|  1.04k|    path_start = i;
11726|  1.04k|    ++i;
11727|  1.04k|    scan_path_run(b, i, len, maybe_dot_segment);
11728|  1.04k|    if (i < len) {
  ------------------
  |  Branch (11728:9): [True: 639, False: 401]
  ------------------
11729|    639|      const uint8_t cls = k_path[b[i]];
11730|    639|      if (cls == 1) {
  ------------------
  |  Branch (11730:11): [True: 95, False: 544]
  ------------------
11731|     95|        path_end = i;
11732|     95|        if (b[i] == '?') {
  ------------------
  |  Branch (11732:13): [True: 59, False: 36]
  ------------------
11733|     59|          query_start = i;
11734|     59|          ++i;
11735|     59|          goto scan_query;
11736|     59|        }
11737|     36|        hash_start = i;
11738|     36|        ++i;
11739|     36|        goto scan_hash;
11740|     95|      }
11741|    544|      rest_simple = false;
11742|  23.2k|      for (; i < len; ++i) {
  ------------------
  |  Branch (11742:14): [True: 22.7k, False: 493]
  ------------------
11743|  22.7k|        if (b[i] == '?') {
  ------------------
  |  Branch (11743:13): [True: 38, False: 22.7k]
  ------------------
11744|     38|          path_end = i;
11745|     38|          query_start = i;
11746|     38|          ++i;
11747|     38|          goto scan_query_boundary;
11748|     38|        }
11749|  22.7k|        if (b[i] == '#') {
  ------------------
  |  Branch (11749:13): [True: 13, False: 22.7k]
  ------------------
11750|     13|          path_end = i;
11751|     13|          hash_start = i;
11752|     13|          ++i;
11753|     13|          goto after_rest;
11754|     13|        }
11755|  22.7k|      }
11756|    493|      path_end = i;
11757|    493|      goto after_rest;
11758|    544|    }
11759|    401|    path_end = i;
11760|    401|  } else if (i < len && b[i] == '?') {
  ------------------
  |  Branch (11760:14): [True: 60, False: 69]
  |  Branch (11760:25): [True: 38, False: 22]
  ------------------
11761|     38|    query_start = i;
11762|     38|    ++i;
11763|     38|    goto scan_query;
11764|     91|  } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (11764:14): [True: 22, False: 69]
  |  Branch (11764:25): [True: 22, False: 0]
  ------------------
11765|     22|    hash_start = i;
11766|     22|    ++i;
11767|     22|    goto scan_hash;
11768|     22|  }
11769|    470|  goto after_rest;
11770|       |
11771|    470|scan_query:
11772|     97|  scan_query_run(b, i, len);
11773|     97|  if (i < len) {
  ------------------
  |  Branch (11773:7): [True: 43, False: 54]
  ------------------
11774|     43|    if (b[i] == '#') {
  ------------------
  |  Branch (11774:9): [True: 20, False: 23]
  ------------------
11775|     20|      hash_start = i;
11776|     20|      ++i;
11777|     20|      goto scan_hash;
11778|     20|    }
11779|     23|    rest_simple = false;
11780|     23|    goto scan_query_boundary;
11781|     43|  }
11782|     54|  goto after_rest;
11783|       |
11784|     61|scan_query_boundary:
11785|    708|  for (; i < len; ++i) {
  ------------------
  |  Branch (11785:10): [True: 661, False: 47]
  ------------------
11786|    661|    if (b[i] == '#') {
  ------------------
  |  Branch (11786:9): [True: 14, False: 647]
  ------------------
11787|     14|      hash_start = i;
11788|     14|      ++i;
11789|     14|      goto after_rest;
11790|     14|    }
11791|    661|  }
11792|     47|  goto after_rest;
11793|       |
11794|     78|scan_hash:
11795|     78|  scan_hash_run(b, i, len);
11796|     78|  if (i < len) {
  ------------------
  |  Branch (11796:7): [True: 13, False: 65]
  ------------------
11797|     13|    rest_simple = false;
11798|     13|  }
11799|       |
11800|  1.16k|after_rest:
11801|  1.16k|  if (rest_simple && maybe_dot_segment) {
  ------------------
  |  Branch (11801:7): [True: 589, False: 580]
  |  Branch (11801:22): [True: 256, False: 333]
  ------------------
11802|    256|    const std::string_view path_body(input.data() + path_start,
11803|    256|                                     path_end - path_start);
11804|    256|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (11804:9): [True: 247, False: 9]
  ------------------
11805|    247|      rest_simple = false;
11806|    247|    }
11807|    256|  }
11808|       |
11809|       |  // The slow path removes ASCII tab/newline anywhere in the input and trims a
11810|       |  // trailing C0 control or space; this fast path does neither. A query or
11811|       |  // fragment reaching the helpers below would keep those bytes percent-encoded
11812|       |  // ("?a\nb" -> "?a%0Ab", "#f " -> "#f%20") instead of stripped, so hand such
11813|       |  // inputs back to the slow path.
11814|  1.16k|  if (!rest_simple && (unicode::is_c0_control_or_space(input.back()) ||
  ------------------
  |  Branch (11814:7): [True: 827, False: 342]
  |  Branch (11814:24): [True: 7, False: 820]
  ------------------
11815|    820|                       unicode::has_tabs_or_newline(input))) {
  ------------------
  |  Branch (11815:24): [True: 20, False: 800]
  ------------------
11816|     27|    return false;
11817|     27|  }
11818|       |
11819|  1.14k|  out.type = scheme_type;
11820|  1.14k|  out.is_valid = true;
11821|  1.14k|  out.has_opaque_path = false;
11822|  1.14k|  out.host_type = DEFAULT;
11823|       |
11824|  1.14k|  const uint32_t port_bytes = (parsed_port != url_components::omitted)
  ------------------
  |  Branch (11824:31): [True: 634, False: 508]
  ------------------
11825|  1.14k|                                  ? (1 + port_decimal_digit_count(parsed_port))
11826|  1.14k|                                  : 0;
11827|       |
11828|  1.14k|  if (!rest_simple) {
  ------------------
  |  Branch (11828:7): [True: 800, False: 342]
  ------------------
11829|    800|    const std::string_view path_view =
11830|    800|        has_path
  ------------------
  |  Branch (11830:9): [True: 785, False: 15]
  ------------------
11831|    800|            ? std::string_view(input.data() + path_start, path_end - path_start)
11832|    800|            : std::string_view{};
11833|    800|    auto apply_query_and_hash = [&]() {
11834|    800|      if (query_start != std::string_view::npos) {
11835|    800|        const size_t q_end =
11836|    800|            (hash_start != std::string_view::npos) ? hash_start : len;
11837|    800|        out.update_base_search(std::string_view(input.data() + query_start + 1,
11838|    800|                                                q_end - query_start - 1),
11839|    800|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
11840|    800|      }
11841|    800|      if (hash_start != std::string_view::npos) {
11842|    800|        out.update_unencoded_base_hash(std::string_view(
11843|    800|            input.data() + hash_start + 1, len - hash_start - 1));
11844|    800|      }
11845|    800|    };
11846|    800|    if constexpr (is_aggregator) {
11847|    800|      out.buffer.assign(input.substr(0, host_end));
11848|    800|      if (parsed_port != url_components::omitted) {
  ------------------
  |  Branch (11848:11): [True: 352, False: 448]
  ------------------
11849|    352|        append_canonical_port(out.buffer, parsed_port);
11850|    352|      }
11851|    800|      if (has_upper) {
  ------------------
  |  Branch (11851:11): [True: 122, False: 678]
  ------------------
11852|    122|        unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
11853|    122|      }
11854|    800|      out.components.protocol_end = protocol_end;
11855|    800|      out.components.username_end = protocol_end + 2;
11856|    800|      out.components.host_start = protocol_end + 2;
11857|    800|      out.components.host_end = static_cast<uint32_t>(host_end);
11858|    800|      out.components.port = parsed_port;
11859|    800|      out.components.pathname_start = static_cast<uint32_t>(out.buffer.size());
11860|    800|      out.components.search_start = url_components::omitted;
11861|    800|      out.components.hash_start = url_components::omitted;
11862|    800|      out.parse_path(path_view);
11863|    800|      apply_query_and_hash();
11864|       |    } else {
11865|       |      std::string host_str(input.substr(host_start, host_len));
11866|       |      if (has_upper) {
11867|       |        unicode::to_lower_ascii(host_str.data(), host_str.size());
11868|       |      }
11869|       |      out.host = std::move(host_str);
11870|       |      if (parsed_port != url_components::omitted) {
11871|       |        out.port = static_cast<uint16_t>(parsed_port);
11872|       |      }
11873|       |      out.parse_path(path_view);
11874|       |      apply_query_and_hash();
11875|       |    }
11876|    800|    return true;
11877|    800|  }
11878|       |
11879|    342|  const bool insert_slash = !has_path;
11880|    342|  if constexpr (is_aggregator) {
11881|    342|    const size_t out_len =
11882|    342|        host_end + port_bytes + (insert_slash ? 1 : 0) + (len - authority_end);
  ------------------
  |  Branch (11882:34): [True: 108, False: 234]
  ------------------
11883|    342|    const bool omit_port_bytes = parsed_port == url_components::omitted;
11884|    342|    if (out_len == len && !insert_slash && !omit_port_bytes) {
  ------------------
  |  Branch (11884:9): [True: 261, False: 81]
  |  Branch (11884:27): [True: 219, False: 42]
  |  Branch (11884:44): [True: 219, False: 0]
  ------------------
11885|    219|      out.buffer.assign(input);
11886|    219|    } else {
11887|    123|      out.buffer.clear();
11888|    123|      out.buffer.reserve(out_len);
11889|    123|      out.buffer.append(input.substr(0, host_end));
11890|    123|      if (parsed_port != url_components::omitted) {
  ------------------
  |  Branch (11890:11): [True: 63, False: 60]
  ------------------
11891|     63|        append_canonical_port(out.buffer, parsed_port);
11892|     63|      }
11893|    123|      if (insert_slash) {
  ------------------
  |  Branch (11893:11): [True: 108, False: 15]
  ------------------
11894|    108|        out.buffer.push_back('/');
11895|    108|      }
11896|    123|      if (authority_end < len) {
  ------------------
  |  Branch (11896:11): [True: 54, False: 69]
  ------------------
11897|     54|        out.buffer.append(input.substr(authority_end));
11898|     54|      }
11899|    123|    }
11900|    342|    if (has_upper) {
  ------------------
  |  Branch (11900:9): [True: 64, False: 278]
  ------------------
11901|     64|      unicode::to_lower_ascii(out.buffer.data() + host_start, host_len);
11902|     64|    }
11903|    342|    const uint32_t pathname_start =
11904|    342|        static_cast<uint32_t>(host_end) + port_bytes;
11905|    342|    const int32_t tail_delta = static_cast<int32_t>(pathname_start) +
11906|    342|                               (insert_slash ? 1 : 0) -
  ------------------
  |  Branch (11906:33): [True: 108, False: 234]
  ------------------
11907|    342|                               static_cast<int32_t>(authority_end);
11908|    342|    out.components.protocol_end = protocol_end;
11909|    342|    out.components.username_end = protocol_end + 2;
11910|    342|    out.components.host_start = protocol_end + 2;
11911|    342|    out.components.host_end = static_cast<uint32_t>(host_end);
11912|    342|    out.components.port = parsed_port;
11913|    342|    out.components.pathname_start = pathname_start;
11914|    342|    out.components.search_start =
11915|    342|        (query_start != std::string_view::npos)
  ------------------
  |  Branch (11915:9): [True: 72, False: 270]
  ------------------
11916|    342|            ? static_cast<uint32_t>(static_cast<int32_t>(query_start) +
11917|     72|                                    tail_delta)
11918|    342|            : url_components::omitted;
11919|    342|    out.components.hash_start =
11920|    342|        (hash_start != std::string_view::npos)
  ------------------
  |  Branch (11920:9): [True: 65, False: 277]
  ------------------
11921|    342|            ? static_cast<uint32_t>(static_cast<int32_t>(hash_start) +
11922|     65|                                    tail_delta)
11923|    342|            : url_components::omitted;
11924|       |  } else {
11925|       |    std::string host_str(input.substr(host_start, host_len));
11926|       |    if (has_upper) {
11927|       |      unicode::to_lower_ascii(host_str.data(), host_str.size());
11928|       |    }
11929|       |    out.host = std::move(host_str);
11930|       |    if (parsed_port != url_components::omitted) {
11931|       |      out.port = static_cast<uint16_t>(parsed_port);
11932|       |    }
11933|       |    if (insert_slash) {
11934|       |      out.path = "/";
11935|       |    } else {
11936|       |      out.path.assign(input.data() + path_start, path_end - path_start);
11937|       |    }
11938|       |    if (query_start != std::string_view::npos) {
11939|       |      const size_t q_end =
11940|       |          (hash_start != std::string_view::npos) ? hash_start : len;
11941|       |      out.query.emplace(input.data() + query_start + 1,
11942|       |                        q_end - query_start - 1);
11943|       |    }
11944|       |    if (hash_start != std::string_view::npos) {
11945|       |      out.hash.emplace(input.data() + hash_start + 1, len - hash_start - 1);
11946|       |    }
11947|       |  }
11948|    342|  return true;
11949|  1.14k|}
_ZN3ada6parser25try_parse_simple_relativeINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERKT_RS8_:
12347|  3.93k|                                                result_type& out) {
12348|  3.93k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
12349|  3.93k|  constexpr bool is_aggregator =
12350|  3.93k|      std::is_same_v<result_type, ada::url_aggregator>;
12351|  3.93k|  static_assert(is_ada_url || is_aggregator);
12352|       |
12353|  3.93k|  if (!base.is_special() || base.type == ada::scheme::type::FILE ||
  ------------------
  |  Branch (12353:7): [True: 2.34k, False: 1.58k]
  |  Branch (12353:29): [True: 362, False: 1.22k]
  ------------------
12354|  2.80k|      base.has_opaque_path || input.empty()) {
  ------------------
  |  Branch (12354:7): [True: 0, False: 1.22k]
  |  Branch (12354:31): [True: 92, False: 1.13k]
  ------------------
12355|  2.80k|    return false;
12356|  2.80k|  }
12357|  1.13k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
12358|  1.13k|  const size_t len = input.size();
12359|  1.13k|  const uint8_t first = b[0];
12360|  1.13k|  const bool path_relative = first != '/' && first != '?' && first != '#';
  ------------------
  |  Branch (12360:30): [True: 1.08k, False: 42]
  |  Branch (12360:46): [True: 1.06k, False: 27]
  |  Branch (12360:62): [True: 1.03k, False: 32]
  ------------------
12361|  1.13k|  if (first == '/' && len > 1 && (b[1] == '/' || b[1] == '\\')) {
  ------------------
  |  Branch (12361:7): [True: 42, False: 1.08k]
  |  Branch (12361:23): [True: 31, False: 11]
  |  Branch (12361:35): [True: 9, False: 22]
  |  Branch (12361:50): [True: 1, False: 21]
  ------------------
12362|     10|    return false;
12363|     10|  }
12364|       |  // A ':' before the first / ? # is a scheme, not a path segment.
12365|  1.12k|  if (path_relative) {
  ------------------
  |  Branch (12365:7): [True: 1.03k, False: 91]
  ------------------
12366|  1.03k|    const size_t delim = input.find_first_of("/?#:");
12367|  1.03k|    if (delim != std::string_view::npos && input[delim] == ':') {
  ------------------
  |  Branch (12367:9): [True: 879, False: 151]
  |  Branch (12367:44): [True: 785, False: 94]
  ------------------
12368|    785|      return false;
12369|    785|    }
12370|  1.03k|  }
12371|       |
12372|    336|  size_t i = 0;
12373|    336|  size_t path_start = 0;
12374|    336|  size_t path_end = 0;
12375|    336|  size_t query_start = std::string_view::npos;
12376|    336|  size_t hash_start = std::string_view::npos;
12377|    336|  bool has_path = false;
12378|    336|  bool maybe_dot_segment = false;
12379|       |
12380|    336|  if (first == '/' || path_relative) {
  ------------------
  |  Branch (12380:7): [True: 32, False: 304]
  |  Branch (12380:23): [True: 245, False: 59]
  ------------------
12381|    277|    has_path = true;
12382|    277|    path_start = 0;
12383|    277|    if (first == '/') {
  ------------------
  |  Branch (12383:9): [True: 32, False: 245]
  ------------------
12384|     32|      i = 1;
12385|     32|    }
12386|    277|    scan_path_run(b, i, len, maybe_dot_segment);
12387|    277|    if (i < len) {
  ------------------
  |  Branch (12387:9): [True: 110, False: 167]
  ------------------
12388|    110|      const uint8_t cls = k_path[b[i]];
12389|    110|      if (cls == 2) {
  ------------------
  |  Branch (12389:11): [True: 84, False: 26]
  ------------------
12390|     84|        return false;
12391|     84|      }
12392|    110|    }
12393|    193|    path_end = i;
12394|    193|    if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12394:9): [True: 26, False: 167]
  |  Branch (12394:20): [True: 20, False: 6]
  ------------------
12395|     20|      query_start = i;
12396|     20|      ++i;
12397|     20|      scan_query_run(b, i, len);
12398|     20|      if (i < len) {
  ------------------
  |  Branch (12398:11): [True: 11, False: 9]
  ------------------
12399|     11|        if (b[i] != '#') {
  ------------------
  |  Branch (12399:13): [True: 4, False: 7]
  ------------------
12400|      4|          return false;
12401|      4|        }
12402|      7|        hash_start = i;
12403|      7|        ++i;
12404|      7|        scan_hash_run(b, i, len);
12405|      7|        if (i < len) {
  ------------------
  |  Branch (12405:13): [True: 1, False: 6]
  ------------------
12406|      1|          return false;
12407|      1|        }
12408|      7|      }
12409|    173|    } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12409:16): [True: 6, False: 167]
  |  Branch (12409:27): [True: 6, False: 0]
  ------------------
12410|      6|      hash_start = i;
12411|      6|      ++i;
12412|      6|      scan_hash_run(b, i, len);
12413|      6|      if (i < len) {
  ------------------
  |  Branch (12413:11): [True: 1, False: 5]
  ------------------
12414|      1|        return false;
12415|      1|      }
12416|    167|    } else if (i < len) {
  ------------------
  |  Branch (12416:16): [True: 0, False: 167]
  ------------------
12417|      0|      return false;
12418|      0|    }
12419|    193|  } else if (first == '?') {
  ------------------
  |  Branch (12419:14): [True: 27, False: 32]
  ------------------
12420|     27|    query_start = 0;
12421|     27|    i = 1;
12422|     27|    scan_query_run(b, i, len);
12423|     27|    if (i < len) {
  ------------------
  |  Branch (12423:9): [True: 19, False: 8]
  ------------------
12424|     19|      if (b[i] != '#') {
  ------------------
  |  Branch (12424:11): [True: 12, False: 7]
  ------------------
12425|     12|        return false;
12426|     12|      }
12427|      7|      hash_start = i;
12428|      7|      ++i;
12429|      7|      scan_hash_run(b, i, len);
12430|      7|      if (i < len) {
  ------------------
  |  Branch (12430:11): [True: 2, False: 5]
  ------------------
12431|      2|        return false;
12432|      2|      }
12433|      7|    }
12434|     32|  } else {
12435|     32|    hash_start = 0;
12436|     32|    i = 1;
12437|     32|    scan_hash_run(b, i, len);
12438|     32|    if (i < len) {
  ------------------
  |  Branch (12438:9): [True: 13, False: 19]
  ------------------
12439|     13|      return false;
12440|     13|    }
12441|     32|  }
12442|       |
12443|    219|  if (has_path && maybe_dot_segment) {
  ------------------
  |  Branch (12443:7): [True: 187, False: 32]
  |  Branch (12443:19): [True: 80, False: 107]
  ------------------
12444|     80|    const std::string_view path_body(input.data() + path_start,
12445|     80|                                     path_end - path_start);
12446|     80|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12446:9): [True: 77, False: 3]
  ------------------
12447|     77|      return false;
12448|     77|    }
12449|     80|  }
12450|       |
12451|    142|  out = base;
12452|    142|  out.is_valid = true;
12453|    142|  out.has_opaque_path = false;
12454|       |
12455|    142|  const size_t q_end =
12456|    142|      (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12456:7): [True: 34, False: 108]
  ------------------
12457|    142|  const bool has_query = query_start != std::string_view::npos;
12458|    142|  const bool has_hash = hash_start != std::string_view::npos;
12459|    142|  const std::string_view query =
12460|    142|      has_query ? std::string_view(input.data() + query_start + 1,
  ------------------
  |  Branch (12460:7): [True: 28, False: 114]
  ------------------
12461|     28|                                   q_end - query_start - 1)
12462|    142|                : std::string_view{};
12463|    142|  const std::string_view hash =
12464|    142|      has_hash ? std::string_view(input.data() + hash_start + 1,
  ------------------
  |  Branch (12464:7): [True: 34, False: 108]
  ------------------
12465|     34|                                  len - hash_start - 1)
12466|    142|               : std::string_view{};
12467|       |
12468|       |  if constexpr (is_aggregator) {
12469|       |    if (first == '/' || first == '?' || path_relative) {
12470|       |      out.clear_hash();
12471|       |      out.clear_search();
12472|       |    }
12473|       |    if (first == '/') {
12474|       |      out.update_base_pathname(input.substr(path_start, path_end - path_start));
12475|       |    } else if (path_relative) {
12476|       |      const std::string_view base_path = out.get_pathname();
12477|       |      const size_t slash = base_path.rfind('/');
12478|       |      const std::string_view prefix = (slash == std::string_view::npos)
12479|       |                                          ? std::string_view("/")
12480|       |                                          : base_path.substr(0, slash + 1);
12481|       |      const std::string_view rel(input.data() + path_start,
12482|       |                                 path_end - path_start);
12483|       |      std::string new_path;
12484|       |      new_path.reserve(prefix.size() + rel.size());
12485|       |      new_path.assign(prefix);
12486|       |      new_path.append(rel);
12487|       |      out.update_base_pathname(new_path);
12488|       |    }
12489|       |    if (has_query) {
12490|       |      // Do not use update_base_search: it strips a leading '?' and would
12491|       |      // collapse '??a=b' into '?a=b'.
12492|       |      out.components.search_start = static_cast<uint32_t>(out.buffer.size());
12493|       |      out.buffer += '?';
12494|       |      out.buffer.append(query);
12495|       |    }
12496|       |    if (has_hash) {
12497|       |      out.update_unencoded_base_hash(hash);
12498|       |    }
12499|    142|  } else {
12500|    142|    if (first == '/' || first == '?' || path_relative) {
  ------------------
  |  Branch (12500:9): [True: 18, False: 124]
  |  Branch (12500:25): [True: 13, False: 111]
  |  Branch (12500:41): [True: 92, False: 19]
  ------------------
12501|    123|      out.hash.reset();
12502|    123|    }
12503|    142|    if (first == '/') {
  ------------------
  |  Branch (12503:9): [True: 18, False: 124]
  ------------------
12504|     18|      out.query.reset();
12505|     18|      out.path.assign(input.substr(path_start, path_end - path_start));
12506|    124|    } else if (path_relative) {
  ------------------
  |  Branch (12506:16): [True: 92, False: 32]
  ------------------
12507|     92|      out.query.reset();
12508|     92|      const size_t slash = out.path.rfind('/');
12509|     92|      if (slash == std::string::npos) {
  ------------------
  |  Branch (12509:11): [True: 0, False: 92]
  ------------------
12510|      0|        out.path = '/';
12511|     92|      } else {
12512|     92|        out.path.resize(slash + 1);
12513|     92|      }
12514|     92|      out.path.append(input.substr(path_start, path_end - path_start));
12515|     92|    }
12516|    142|    if (has_query) {
  ------------------
  |  Branch (12516:9): [True: 28, False: 114]
  ------------------
12517|     28|      out.query.emplace(query);
12518|     28|    }
12519|    142|    if (has_hash) {
  ------------------
  |  Branch (12519:9): [True: 34, False: 108]
  ------------------
12520|     34|      out.hash.emplace(hash);
12521|     34|    }
12522|    142|  }
12523|    142|  return true;
12524|    219|}
_ZN3ada6parser25try_parse_simple_relativeINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERKT_RS8_:
12347|  3.93k|                                                result_type& out) {
12348|  3.93k|  constexpr bool is_ada_url = std::is_same_v<result_type, ada::url>;
12349|  3.93k|  constexpr bool is_aggregator =
12350|  3.93k|      std::is_same_v<result_type, ada::url_aggregator>;
12351|  3.93k|  static_assert(is_ada_url || is_aggregator);
12352|       |
12353|  3.93k|  if (!base.is_special() || base.type == ada::scheme::type::FILE ||
  ------------------
  |  Branch (12353:7): [True: 2.34k, False: 1.58k]
  |  Branch (12353:29): [True: 362, False: 1.22k]
  ------------------
12354|  2.80k|      base.has_opaque_path || input.empty()) {
  ------------------
  |  Branch (12354:7): [True: 0, False: 1.22k]
  |  Branch (12354:31): [True: 92, False: 1.13k]
  ------------------
12355|  2.80k|    return false;
12356|  2.80k|  }
12357|  1.13k|  const auto* b = reinterpret_cast<const uint8_t*>(input.data());
12358|  1.13k|  const size_t len = input.size();
12359|  1.13k|  const uint8_t first = b[0];
12360|  1.13k|  const bool path_relative = first != '/' && first != '?' && first != '#';
  ------------------
  |  Branch (12360:30): [True: 1.08k, False: 42]
  |  Branch (12360:46): [True: 1.06k, False: 27]
  |  Branch (12360:62): [True: 1.03k, False: 32]
  ------------------
12361|  1.13k|  if (first == '/' && len > 1 && (b[1] == '/' || b[1] == '\\')) {
  ------------------
  |  Branch (12361:7): [True: 42, False: 1.08k]
  |  Branch (12361:23): [True: 31, False: 11]
  |  Branch (12361:35): [True: 9, False: 22]
  |  Branch (12361:50): [True: 1, False: 21]
  ------------------
12362|     10|    return false;
12363|     10|  }
12364|       |  // A ':' before the first / ? # is a scheme, not a path segment.
12365|  1.12k|  if (path_relative) {
  ------------------
  |  Branch (12365:7): [True: 1.03k, False: 91]
  ------------------
12366|  1.03k|    const size_t delim = input.find_first_of("/?#:");
12367|  1.03k|    if (delim != std::string_view::npos && input[delim] == ':') {
  ------------------
  |  Branch (12367:9): [True: 879, False: 151]
  |  Branch (12367:44): [True: 785, False: 94]
  ------------------
12368|    785|      return false;
12369|    785|    }
12370|  1.03k|  }
12371|       |
12372|    336|  size_t i = 0;
12373|    336|  size_t path_start = 0;
12374|    336|  size_t path_end = 0;
12375|    336|  size_t query_start = std::string_view::npos;
12376|    336|  size_t hash_start = std::string_view::npos;
12377|    336|  bool has_path = false;
12378|    336|  bool maybe_dot_segment = false;
12379|       |
12380|    336|  if (first == '/' || path_relative) {
  ------------------
  |  Branch (12380:7): [True: 32, False: 304]
  |  Branch (12380:23): [True: 245, False: 59]
  ------------------
12381|    277|    has_path = true;
12382|    277|    path_start = 0;
12383|    277|    if (first == '/') {
  ------------------
  |  Branch (12383:9): [True: 32, False: 245]
  ------------------
12384|     32|      i = 1;
12385|     32|    }
12386|    277|    scan_path_run(b, i, len, maybe_dot_segment);
12387|    277|    if (i < len) {
  ------------------
  |  Branch (12387:9): [True: 110, False: 167]
  ------------------
12388|    110|      const uint8_t cls = k_path[b[i]];
12389|    110|      if (cls == 2) {
  ------------------
  |  Branch (12389:11): [True: 84, False: 26]
  ------------------
12390|     84|        return false;
12391|     84|      }
12392|    110|    }
12393|    193|    path_end = i;
12394|    193|    if (i < len && b[i] == '?') {
  ------------------
  |  Branch (12394:9): [True: 26, False: 167]
  |  Branch (12394:20): [True: 20, False: 6]
  ------------------
12395|     20|      query_start = i;
12396|     20|      ++i;
12397|     20|      scan_query_run(b, i, len);
12398|     20|      if (i < len) {
  ------------------
  |  Branch (12398:11): [True: 11, False: 9]
  ------------------
12399|     11|        if (b[i] != '#') {
  ------------------
  |  Branch (12399:13): [True: 4, False: 7]
  ------------------
12400|      4|          return false;
12401|      4|        }
12402|      7|        hash_start = i;
12403|      7|        ++i;
12404|      7|        scan_hash_run(b, i, len);
12405|      7|        if (i < len) {
  ------------------
  |  Branch (12405:13): [True: 1, False: 6]
  ------------------
12406|      1|          return false;
12407|      1|        }
12408|      7|      }
12409|    173|    } else if (i < len && b[i] == '#') {
  ------------------
  |  Branch (12409:16): [True: 6, False: 167]
  |  Branch (12409:27): [True: 6, False: 0]
  ------------------
12410|      6|      hash_start = i;
12411|      6|      ++i;
12412|      6|      scan_hash_run(b, i, len);
12413|      6|      if (i < len) {
  ------------------
  |  Branch (12413:11): [True: 1, False: 5]
  ------------------
12414|      1|        return false;
12415|      1|      }
12416|    167|    } else if (i < len) {
  ------------------
  |  Branch (12416:16): [True: 0, False: 167]
  ------------------
12417|      0|      return false;
12418|      0|    }
12419|    193|  } else if (first == '?') {
  ------------------
  |  Branch (12419:14): [True: 27, False: 32]
  ------------------
12420|     27|    query_start = 0;
12421|     27|    i = 1;
12422|     27|    scan_query_run(b, i, len);
12423|     27|    if (i < len) {
  ------------------
  |  Branch (12423:9): [True: 19, False: 8]
  ------------------
12424|     19|      if (b[i] != '#') {
  ------------------
  |  Branch (12424:11): [True: 12, False: 7]
  ------------------
12425|     12|        return false;
12426|     12|      }
12427|      7|      hash_start = i;
12428|      7|      ++i;
12429|      7|      scan_hash_run(b, i, len);
12430|      7|      if (i < len) {
  ------------------
  |  Branch (12430:11): [True: 2, False: 5]
  ------------------
12431|      2|        return false;
12432|      2|      }
12433|      7|    }
12434|     32|  } else {
12435|     32|    hash_start = 0;
12436|     32|    i = 1;
12437|     32|    scan_hash_run(b, i, len);
12438|     32|    if (i < len) {
  ------------------
  |  Branch (12438:9): [True: 13, False: 19]
  ------------------
12439|     13|      return false;
12440|     13|    }
12441|     32|  }
12442|       |
12443|    219|  if (has_path && maybe_dot_segment) {
  ------------------
  |  Branch (12443:7): [True: 187, False: 32]
  |  Branch (12443:19): [True: 80, False: 107]
  ------------------
12444|     80|    const std::string_view path_body(input.data() + path_start,
12445|     80|                                     path_end - path_start);
12446|     80|    if (path_has_dot_segment(path_body)) {
  ------------------
  |  Branch (12446:9): [True: 77, False: 3]
  ------------------
12447|     77|      return false;
12448|     77|    }
12449|     80|  }
12450|       |
12451|    142|  out = base;
12452|    142|  out.is_valid = true;
12453|    142|  out.has_opaque_path = false;
12454|       |
12455|    142|  const size_t q_end =
12456|    142|      (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12456:7): [True: 34, False: 108]
  ------------------
12457|    142|  const bool has_query = query_start != std::string_view::npos;
12458|    142|  const bool has_hash = hash_start != std::string_view::npos;
12459|    142|  const std::string_view query =
12460|    142|      has_query ? std::string_view(input.data() + query_start + 1,
  ------------------
  |  Branch (12460:7): [True: 28, False: 114]
  ------------------
12461|     28|                                   q_end - query_start - 1)
12462|    142|                : std::string_view{};
12463|    142|  const std::string_view hash =
12464|    142|      has_hash ? std::string_view(input.data() + hash_start + 1,
  ------------------
  |  Branch (12464:7): [True: 34, False: 108]
  ------------------
12465|     34|                                  len - hash_start - 1)
12466|    142|               : std::string_view{};
12467|       |
12468|    142|  if constexpr (is_aggregator) {
12469|    142|    if (first == '/' || first == '?' || path_relative) {
  ------------------
  |  Branch (12469:9): [True: 18, False: 124]
  |  Branch (12469:25): [True: 13, False: 111]
  |  Branch (12469:41): [True: 92, False: 19]
  ------------------
12470|    123|      out.clear_hash();
12471|    123|      out.clear_search();
12472|    123|    }
12473|    142|    if (first == '/') {
  ------------------
  |  Branch (12473:9): [True: 18, False: 124]
  ------------------
12474|     18|      out.update_base_pathname(input.substr(path_start, path_end - path_start));
12475|    124|    } else if (path_relative) {
  ------------------
  |  Branch (12475:16): [True: 92, False: 32]
  ------------------
12476|     92|      const std::string_view base_path = out.get_pathname();
12477|     92|      const size_t slash = base_path.rfind('/');
12478|     92|      const std::string_view prefix = (slash == std::string_view::npos)
  ------------------
  |  Branch (12478:39): [True: 0, False: 92]
  ------------------
12479|     92|                                          ? std::string_view("/")
12480|     92|                                          : base_path.substr(0, slash + 1);
12481|     92|      const std::string_view rel(input.data() + path_start,
12482|     92|                                 path_end - path_start);
12483|     92|      std::string new_path;
12484|     92|      new_path.reserve(prefix.size() + rel.size());
12485|     92|      new_path.assign(prefix);
12486|     92|      new_path.append(rel);
12487|     92|      out.update_base_pathname(new_path);
12488|     92|    }
12489|    142|    if (has_query) {
  ------------------
  |  Branch (12489:9): [True: 28, False: 114]
  ------------------
12490|       |      // Do not use update_base_search: it strips a leading '?' and would
12491|       |      // collapse '??a=b' into '?a=b'.
12492|     28|      out.components.search_start = static_cast<uint32_t>(out.buffer.size());
12493|     28|      out.buffer += '?';
12494|     28|      out.buffer.append(query);
12495|     28|    }
12496|    142|    if (has_hash) {
  ------------------
  |  Branch (12496:9): [True: 34, False: 108]
  ------------------
12497|     34|      out.update_unencoded_base_hash(hash);
12498|     34|    }
12499|       |  } else {
12500|       |    if (first == '/' || first == '?' || path_relative) {
12501|       |      out.hash.reset();
12502|       |    }
12503|       |    if (first == '/') {
12504|       |      out.query.reset();
12505|       |      out.path.assign(input.substr(path_start, path_end - path_start));
12506|       |    } else if (path_relative) {
12507|       |      out.query.reset();
12508|       |      const size_t slash = out.path.rfind('/');
12509|       |      if (slash == std::string::npos) {
12510|       |        out.path = '/';
12511|       |      } else {
12512|       |        out.path.resize(slash + 1);
12513|       |      }
12514|       |      out.path.append(input.substr(path_start, path_end - path_start));
12515|       |    }
12516|       |    if (has_query) {
12517|       |      out.query.emplace(query);
12518|       |    }
12519|       |    if (has_hash) {
12520|       |      out.hash.emplace(hash);
12521|       |    }
12522|       |  }
12523|    142|  return true;
12524|    219|}
_ZN3ada6parser14parse_url_implINS_3urlELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
12528|  46.5k|                           const result_type* base_url) {
12529|       |  // We can specialize the implementation per type.
12530|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
12531|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
12532|       |  // something else } is free (at runtime). This means that ada::url_aggregator
12533|       |  // and ada::url **do not have to support the exact same API**.
12534|  46.5k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
12535|  46.5k|  constexpr bool result_type_is_ada_url_aggregator =
12536|  46.5k|      std::is_same_v<url_aggregator, result_type>;
12537|  46.5k|  static_assert(result_type_is_ada_url ||
12538|  46.5k|                result_type_is_ada_url_aggregator);  // We don't support
12539|       |                                                     // anything else for now.
12540|       |
12541|  46.5k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
12542|  46.5k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
12543|  46.5k|          ")");
12544|       |
12545|  46.5k|  result_type url{};
12546|       |
12547|  46.5k|  const uint32_t max_input_length = ada::get_max_input_length();
12548|       |
12549|       |  // Normalization (percent-encoding, IDNA) can push the serialized URL past
12550|       |  // max_input_length even when the input fit under it. The check at the end of
12551|       |  // the state machine covers URLs that run to completion, but several states
12552|       |  // return early after appending a query or fragment, so run the same check on
12553|       |  // those exits too. Without this, a query- or fragment-terminated URL such as
12554|       |  // file://x/?<...> or https://user@x/?<...> is accepted while the equivalent
12555|       |  // https://x/?<...> that takes the absolute fast path is rejected.
12556|  46.5k|  const auto enforce_max_input_length = [&]() {
12557|  46.5k|    if constexpr (store_values) {
12558|  46.5k|      if (url.is_valid) {
12559|  46.5k|        if constexpr (result_type_is_ada_url_aggregator) {
12560|  46.5k|          if (url.buffer.size() > max_input_length) [[unlikely]] {
12561|  46.5k|            url.is_valid = false;
12562|  46.5k|          }
12563|  46.5k|        } else {
12564|  46.5k|          if (url.get_href_size() > max_input_length) [[unlikely]] {
12565|  46.5k|            url.is_valid = false;
12566|  46.5k|          }
12567|  46.5k|        }
12568|  46.5k|      }
12569|  46.5k|    }
12570|  46.5k|  };
12571|       |
12572|       |  // We refuse to parse URL strings that exceed the maximum input length.
12573|       |  // By default, this is 4GB but can be configured via
12574|       |  // ada::set_max_input_length().
12575|  46.5k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12575:7): [True: 0, False: 46.5k]
  ------------------
12576|      0|    url.is_valid = false;
12577|      0|  }
12578|       |  // Going forward, user_input.size() is in [0,
12579|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
12580|       |  // base, or the optional_url was invalid, we must return.
12581|  46.5k|  if (base_url != nullptr) {
  ------------------
  |  Branch (12581:7): [True: 3.93k, False: 42.6k]
  ------------------
12582|  3.93k|    url.is_valid &= base_url->is_valid;
12583|  3.93k|  }
12584|  46.5k|  if (!url.is_valid) {
  ------------------
  |  Branch (12584:7): [True: 0, False: 46.5k]
  ------------------
12585|      0|    return url;
12586|      0|  }
12587|       |
12588|       |  // Simple absolute / relative fast paths (before tabs/newline scan).
12589|  46.5k|  if constexpr (store_values) {
12590|  46.5k|    bool hit_fast_path = false;
12591|  46.5k|    if (base_url == nullptr) {
  ------------------
  |  Branch (12591:9): [True: 42.6k, False: 3.93k]
  ------------------
12592|       |      // IPv4/IPv6 and userinfo miss the fast path. Skip the never_inline
12593|       |      // call so those URLs (including SetHref) do not pay for a miss.
12594|  42.6k|      const auto* p = reinterpret_cast<const uint8_t*>(user_input.data());
12595|  42.6k|      const size_t n = user_input.size();
12596|  42.6k|      size_t host_start = 0;
12597|  42.6k|      if (n >= 8 && p[4] == ':' && p[5] == '/' && p[6] == '/') {
  ------------------
  |  Branch (12597:11): [True: 16.3k, False: 26.2k]
  |  Branch (12597:21): [True: 3.02k, False: 13.3k]
  |  Branch (12597:36): [True: 2.30k, False: 723]
  |  Branch (12597:51): [True: 2.28k, False: 24]
  ------------------
12598|  2.28k|        host_start = 7;
12599|  40.3k|      } else if (n >= 9 && p[5] == ':' && p[6] == '/' && p[7] == '/') {
  ------------------
  |  Branch (12599:18): [True: 12.7k, False: 27.5k]
  |  Branch (12599:28): [True: 240, False: 12.4k]
  |  Branch (12599:43): [True: 43, False: 197]
  |  Branch (12599:58): [True: 27, False: 16]
  ------------------
12600|     27|        host_start = 8;
12601|     27|      }
12602|  42.6k|      const uint8_t host_first = host_start != 0 ? p[host_start] : 0;
  ------------------
  |  Branch (12602:34): [True: 2.30k, False: 40.3k]
  ------------------
12603|  42.6k|      const bool skip_ip =
12604|  42.6k|          host_first == '[' || (host_first >= '0' && host_first <= '9');
  ------------------
  |  Branch (12604:11): [True: 34, False: 42.5k]
  |  Branch (12604:33): [True: 1.66k, False: 40.9k]
  |  Branch (12604:54): [True: 563, False: 1.09k]
  ------------------
12605|  42.6k|      const bool skip_userinfo =
12606|  42.6k|          host_start != 0 && authority_has_at(p, host_start, n);
  ------------------
  |  Branch (12606:11): [True: 2.30k, False: 40.3k]
  |  Branch (12606:30): [True: 22, False: 2.28k]
  ------------------
12607|  42.6k|      hit_fast_path = !skip_ip && !skip_userinfo &&
  ------------------
  |  Branch (12607:23): [True: 42.0k, False: 597]
  |  Branch (12607:35): [True: 41.9k, False: 22]
  ------------------
12608|  41.9k|                      try_parse_simple_absolute(user_input, url);
  ------------------
  |  Branch (12608:23): [True: 4.23k, False: 37.7k]
  ------------------
12609|  42.6k|    } else {
12610|  3.93k|      hit_fast_path = try_parse_simple_relative(user_input, *base_url, url);
12611|  3.93k|    }
12612|  46.5k|    if (hit_fast_path) {
  ------------------
  |  Branch (12612:9): [True: 4.37k, False: 42.1k]
  ------------------
12613|       |      if constexpr (result_type_is_ada_url_aggregator) {
12614|       |        if (url.buffer.size() > max_input_length) [[unlikely]] {
12615|       |          url.is_valid = false;
12616|       |        }
12617|  4.37k|      } else {
12618|       |        // For a small absolute input, even worst-case normalization cannot
12619|       |        // exceed the limit: percent encoding expands at most 3x and IDNA at
12620|       |        // most 4.5x. Avoid traversing every stored component on the common
12621|       |        // default-limit path.
12622|  4.37k|        if ((base_url != nullptr ||
  ------------------
  |  Branch (12622:14): [True: 142, False: 4.23k]
  ------------------
12623|  4.23k|             user_input.size() > static_cast<size_t>(max_input_length) / 5) &&
  ------------------
  |  Branch (12623:14): [True: 0, False: 4.23k]
  ------------------
12624|    142|            url.get_href_size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12624:13): [True: 0, False: 142]
  ------------------
12625|      0|          url.is_valid = false;
12626|      0|        }
12627|  4.37k|      }
12628|  4.37k|      return url;
12629|  4.37k|    }
12630|  46.5k|  }
12631|       |
12632|  42.1k|  state state = state::SCHEME_START;
12633|       |
12634|  46.5k|  std::string tmp_buffer;
12635|  46.5k|  std::string_view url_data;
12636|  46.5k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (12636:7): [True: 296, False: 46.2k]
  ------------------
12637|    296|    tmp_buffer = user_input;
12638|       |    // Optimization opportunity: Instead of copying and then pruning, we could
12639|       |    // just directly build the string from user_input.
12640|    296|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
12641|    296|    url_data = tmp_buffer;
12642|  46.2k|  } else [[likely]] {
12643|  46.2k|    url_data = user_input;
12644|  46.2k|  }
12645|       |
12646|       |  // Leading and trailing control characters are uncommon and easy to deal with
12647|       |  // (no performance concern).
12648|  46.5k|  helpers::trim_c0_whitespace(url_data);
12649|       |
12650|       |  if constexpr (result_type_is_ada_url_aggregator && store_values) {
12651|       |    // Most of the time, we just need user_input.size().
12652|       |    // In some instances, we may need a bit more.
12653|       |    ///////////////////////////
12654|       |    // This is *very* important. This line should *not* be removed
12655|       |    // hastily. There are principled reasons why reserve is important
12656|       |    // for performance. If you have a benchmark with small inputs,
12657|       |    // it may not matter, but in other instances, it could.
12658|       |    ////
12659|       |    // This rounds up to the next power of two.
12660|       |    // We know that user_input.size() is in [0,
12661|       |    // std::numeric_limits<uint32_t>::max).
12662|       |    uint32_t reserve_capacity =
12663|       |        (0xFFFFFFFF >>
12664|       |         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
12665|       |        1;
12666|       |    url.reserve(reserve_capacity);
12667|       |  }
12668|       |
12669|       |  // Optimization opportunity. Most websites do not have fragment.
12670|  46.5k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
12671|       |  // We add it last so that an implementation like ada::url_aggregator
12672|       |  // can append it last to its internal buffer, thus improving performance.
12673|       |
12674|       |  // Here url_data no longer has its fragment.
12675|       |  // We are going to access the data from url_data (it is immutable).
12676|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
12677|       |  // The input_position variable should range from 0 to input_size.
12678|       |  // It is illegal to access url_data at input_size.
12679|  46.5k|  size_t input_position = 0;
12680|  46.5k|  const size_t input_size = url_data.size();
12681|       |  // Keep running the following state machine by switching on state.
12682|       |  // If after a run pointer points to the EOF code point, go to the next step.
12683|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
12684|       |  // We never decrement input_position.
12685|   173k|  while (input_position <= input_size) {
  ------------------
  |  Branch (12685:10): [True: 158k, False: 15.4k]
  ------------------
12686|   158k|    ada_log("In parsing at ", input_position, " out of ", input_size,
12687|   158k|            " in state ", ada::to_string(state));
12688|   158k|    switch (state) {
12689|  42.1k|      case state::SCHEME_START: {
  ------------------
  |  Branch (12689:7): [True: 42.1k, False: 116k]
  ------------------
12690|  42.1k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
12691|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
12692|       |        // state to scheme state.
12693|  42.1k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (12693:13): [True: 22.2k, False: 19.8k]
  ------------------
12694|  22.2k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (12694:13): [True: 21.2k, False: 998]
  ------------------
12695|  21.2k|          state = state::SCHEME;
12696|  21.2k|          input_position++;
12697|  21.2k|        } else {
12698|       |          // Otherwise, if state override is not given, set state to no scheme
12699|       |          // state and decrease pointer by 1.
12700|  20.8k|          state = state::NO_SCHEME;
12701|  20.8k|        }
12702|  42.1k|        break;
12703|      0|      }
12704|  21.2k|      case state::SCHEME: {
  ------------------
  |  Branch (12704:7): [True: 21.2k, False: 137k]
  ------------------
12705|  21.2k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
12706|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
12707|       |        // append c, lowercased, to buffer.
12708|  50.5k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (12708:16): [True: 50.2k, False: 295]
  ------------------
12709|  50.2k|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (12709:16): [True: 29.2k, False: 20.9k]
  ------------------
12710|  29.2k|          input_position++;
12711|  29.2k|        }
12712|       |        // Otherwise, if c is U+003A (:), then:
12713|  21.2k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (12713:13): [True: 20.9k, False: 295]
  ------------------
12714|  20.9k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (12714:13): [True: 20.7k, False: 255]
  ------------------
12715|  20.7k|          ada_log("SCHEME the scheme should be ",
12716|  20.7k|                  url_data.substr(0, input_position));
12717|  20.7k|          if constexpr (result_type_is_ada_url) {
12718|  20.7k|            if (!url.parse_scheme(url_data.substr(0, input_position))) {
  ------------------
  |  Branch (12718:17): [True: 0, False: 20.7k]
  ------------------
12719|      0|              return url;
12720|      0|            }
12721|       |          } else {
12722|       |            // we pass the colon along instead of painfully adding it back.
12723|       |            if (!url.parse_scheme_with_colon(
12724|       |                    url_data.substr(0, input_position + 1))) {
12725|       |              return url;
12726|       |            }
12727|       |          }
12728|  20.7k|          ada_log("SCHEME the scheme is ", url.get_protocol());
12729|       |
12730|       |          // If url's scheme is "file", then:
12731|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
12732|  20.7k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (12732:15): [True: 2.64k, False: 18.0k]
  ------------------
12733|       |            // Set state to file state.
12734|  2.64k|            state = state::FILE;
12735|  2.64k|          }
12736|       |          // Otherwise, if url is special, base is non-null, and base's scheme
12737|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
12738|       |          // != nullptr is false.
12739|  18.0k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (12739:20): [True: 12.5k, False: 5.54k]
  |  Branch (12739:40): [True: 2.12k, False: 10.4k]
  ------------------
12740|  2.12k|                   base_url->type == url.type) {
  ------------------
  |  Branch (12740:20): [True: 280, False: 1.84k]
  ------------------
12741|       |            // Set state to special relative or authority state.
12742|    280|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
12743|    280|          }
12744|       |          // Otherwise, if url is special, set state to special authority
12745|       |          // slashes state.
12746|  17.7k|          else if (url.is_special()) {
  ------------------
  |  Branch (12746:20): [True: 12.2k, False: 5.54k]
  ------------------
12747|  12.2k|            state = state::SPECIAL_AUTHORITY_SLASHES;
12748|  12.2k|          }
12749|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
12750|       |          // path or authority state and increase pointer by 1.
12751|  5.54k|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (12751:20): [True: 3.19k, False: 2.35k]
  ------------------
12752|  3.19k|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (12752:20): [True: 2.21k, False: 980]
  ------------------
12753|  2.21k|            state = state::PATH_OR_AUTHORITY;
12754|  2.21k|            input_position++;
12755|  2.21k|          }
12756|       |          // Otherwise, set url's path to the empty string and set state to
12757|       |          // opaque path state.
12758|  3.33k|          else {
12759|  3.33k|            state = state::OPAQUE_PATH;
12760|  3.33k|          }
12761|  20.7k|        }
12762|       |        // Otherwise, if state override is not given, set buffer to the empty
12763|       |        // string, state to no scheme state, and start over (from the first code
12764|       |        // point in input).
12765|    550|        else {
12766|    550|          state = state::NO_SCHEME;
12767|    550|          input_position = 0;
12768|    550|          break;
12769|    550|        }
12770|  20.7k|        input_position++;
12771|  20.7k|        break;
12772|  21.2k|      }
12773|  21.4k|      case state::NO_SCHEME: {
  ------------------
  |  Branch (12773:7): [True: 21.4k, False: 136k]
  ------------------
12774|  21.4k|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
12775|       |        // The fragment was pruned from url_data before the state machine ran,
12776|       |        // so 'c is U+0023 (#)' holds exactly when a fragment was found and
12777|       |        // nothing else remains in front of it.
12778|  21.4k|        const bool c_is_hash =
12779|  21.4k|            fragment.has_value() && input_position == input_size;
  ------------------
  |  Branch (12779:13): [True: 248, False: 21.1k]
  |  Branch (12779:37): [True: 196, False: 52]
  ------------------
12780|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
12781|       |        // validation error, return failure.
12782|  21.4k|        if (base_url == nullptr || (base_url->has_opaque_path && !c_is_hash)) {
  ------------------
  |  Branch (12782:13): [True: 20.6k, False: 838]
  |  Branch (12782:37): [True: 118, False: 720]
  |  Branch (12782:66): [True: 47, False: 71]
  ------------------
12783|  20.6k|          ada_log("NO_SCHEME validation error");
12784|  20.6k|          url.is_valid = false;
12785|  20.6k|          return url;
12786|  20.6k|        }
12787|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
12788|       |        // set url's scheme to base's scheme, url's path to base's path, url's
12789|       |        // query to base's query, and set state to fragment state.
12790|    791|        else if (base_url->has_opaque_path && c_is_hash) {
  ------------------
  |  Branch (12790:18): [True: 71, False: 720]
  |  Branch (12790:47): [True: 71, False: 0]
  ------------------
12791|     71|          ada_log("NO_SCHEME opaque base with fragment");
12792|     71|          url.copy_scheme(*base_url);
12793|     71|          url.has_opaque_path = base_url->has_opaque_path;
12794|       |
12795|     71|          if constexpr (result_type_is_ada_url) {
12796|     71|            url.path = base_url->path;
12797|     71|            url.query = base_url->query;
12798|       |          } else {
12799|       |            url.update_base_pathname(base_url->get_pathname());
12800|       |            if (base_url->has_search()) {
12801|       |              // get_search() returns "" for an empty query string (URL ends
12802|       |              // with '?'). update_base_search("") would incorrectly clear the
12803|       |              // query, so pass "?" to preserve the empty query distinction.
12804|       |              auto s = base_url->get_search();
12805|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
12806|       |            }
12807|       |          }
12808|     71|          url.update_unencoded_base_hash(*fragment);
12809|     71|          enforce_max_input_length();
12810|     71|          return url;
12811|     71|        }
12812|       |        // Otherwise, if base's scheme is not "file", set state to relative
12813|       |        // state and decrease pointer by 1.
12814|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
12815|    720|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (12815:18): [True: 497, False: 223]
  ------------------
12816|    497|          ada_log("NO_SCHEME non-file relative path");
12817|    497|          state = state::RELATIVE_SCHEME;
12818|    497|        }
12819|       |        // Otherwise, set state to file state and decrease pointer by 1.
12820|    223|        else {
12821|    223|          ada_log("NO_SCHEME file base type");
12822|    223|          state = state::FILE;
12823|    223|        }
12824|    720|        break;
12825|  21.4k|      }
12826|  13.3k|      case state::AUTHORITY: {
  ------------------
  |  Branch (12826:7): [True: 13.3k, False: 144k]
  ------------------
12827|  13.3k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
12828|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
12829|       |        // about AUTHORITY. Of course, we could have @ and still not have to
12830|       |        // worry about AUTHORITY.
12831|       |        // TODO: Instead of just collecting a bool, collect the location of the
12832|       |        // '@' and do something useful with it.
12833|       |        // TODO: We could do various processing early on, using a single pass
12834|       |        // over the string to collect information about it, e.g., telling us
12835|       |        // whether there is a @ and if so, where (or how many).
12836|       |
12837|       |        // Check if url data contains an @.
12838|  13.3k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (12838:13): [True: 12.1k, False: 1.22k]
  ------------------
12839|  12.1k|          state = state::HOST;
12840|  12.1k|          break;
12841|  12.1k|        }
12842|  1.22k|        bool at_sign_seen{false};
12843|  1.22k|        bool password_token_seen{false};
12844|       |        /**
12845|       |         * We expect something of the sort...
12846|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
12847|       |         * --------^
12848|       |         */
12849|  8.66k|        do {
12850|  8.66k|          std::string_view view = url_data.substr(input_position);
12851|       |          // The delimiters are @, /, ? \\.
12852|  8.66k|          size_t location =
12853|  8.66k|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (12853:15): [True: 7.70k, False: 957]
  ------------------
12854|  8.66k|                               : helpers::find_authority_delimiter(view);
12855|  8.66k|          std::string_view authority_view = view.substr(0, location);
12856|  8.66k|          size_t end_of_authority = input_position + authority_view.size();
12857|       |          // If c is U+0040 (@), then:
12858|  8.66k|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (12858:15): [True: 7.85k, False: 813]
  ------------------
12859|  7.85k|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (12859:15): [True: 7.43k, False: 416]
  ------------------
12860|       |            // If atSignSeen is true, then prepend "%40" to buffer.
12861|  7.43k|            if (at_sign_seen) {
  ------------------
  |  Branch (12861:17): [True: 6.30k, False: 1.13k]
  ------------------
12862|  6.30k|              if (password_token_seen) {
  ------------------
  |  Branch (12862:19): [True: 2.49k, False: 3.81k]
  ------------------
12863|  2.49k|                if constexpr (result_type_is_ada_url) {
12864|  2.49k|                  url.password += "%40";
12865|       |                } else {
12866|       |                  url.append_base_password("%40");
12867|       |                }
12868|  3.81k|              } else {
12869|  3.81k|                if constexpr (result_type_is_ada_url) {
12870|  3.81k|                  url.username += "%40";
12871|       |                } else {
12872|       |                  url.append_base_username("%40");
12873|       |                }
12874|  3.81k|              }
12875|  6.30k|            }
12876|       |
12877|  7.43k|            at_sign_seen = true;
12878|       |
12879|  7.43k|            if (!password_token_seen) {
  ------------------
  |  Branch (12879:17): [True: 4.94k, False: 2.49k]
  ------------------
12880|  4.94k|              size_t password_token_location = authority_view.find(':');
12881|  4.94k|              password_token_seen =
12882|  4.94k|                  password_token_location != std::string_view::npos;
12883|       |
12884|  4.94k|              if constexpr (store_values) {
12885|  4.94k|                if (!password_token_seen) {
  ------------------
  |  Branch (12885:21): [True: 4.53k, False: 406]
  ------------------
12886|  4.53k|                  if constexpr (result_type_is_ada_url) {
12887|  4.53k|                    url.username += unicode::percent_encode(
12888|  4.53k|                        authority_view,
12889|  4.53k|                        character_sets::USERINFO_PERCENT_ENCODE);
12890|       |                  } else {
12891|       |                    url.append_base_username(unicode::percent_encode(
12892|       |                        authority_view,
12893|       |                        character_sets::USERINFO_PERCENT_ENCODE));
12894|       |                  }
12895|  4.53k|                } else {
12896|    406|                  if constexpr (result_type_is_ada_url) {
12897|    406|                    url.username += unicode::percent_encode(
12898|    406|                        authority_view.substr(0, password_token_location),
12899|    406|                        character_sets::USERINFO_PERCENT_ENCODE);
12900|    406|                    url.password += unicode::percent_encode(
12901|    406|                        authority_view.substr(password_token_location + 1),
12902|    406|                        character_sets::USERINFO_PERCENT_ENCODE);
12903|       |                  } else {
12904|       |                    url.append_base_username(unicode::percent_encode(
12905|       |                        authority_view.substr(0, password_token_location),
12906|       |                        character_sets::USERINFO_PERCENT_ENCODE));
12907|       |                    url.append_base_password(unicode::percent_encode(
12908|       |                        authority_view.substr(password_token_location + 1),
12909|       |                        character_sets::USERINFO_PERCENT_ENCODE));
12910|       |                  }
12911|    406|                }
12912|  4.94k|              }
12913|  4.94k|            } else if constexpr (store_values) {
12914|  2.49k|              if constexpr (result_type_is_ada_url) {
12915|  2.49k|                url.password += unicode::percent_encode(
12916|  2.49k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
12917|       |              } else {
12918|       |                url.append_base_password(unicode::percent_encode(
12919|       |                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
12920|       |              }
12921|  2.49k|            }
12922|  7.43k|          }
12923|       |          // Otherwise, if one of the following is true:
12924|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
12925|       |          // - url is special and c is U+005C (\)
12926|  1.22k|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (12926:20): [True: 813, False: 416]
  ------------------
12927|    416|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (12927:20): [True: 376, False: 40]
  ------------------
12928|     40|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (12928:20): [True: 34, False: 6]
  ------------------
12929|  1.22k|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (12929:21): [True: 6, False: 0]
  |  Branch (12929:41): [True: 6, False: 0]
  ------------------
12930|       |            // If atSignSeen is true and authority_view is the empty string,
12931|       |            // validation error, return failure.
12932|  1.22k|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (12932:17): [True: 1.13k, False: 94]
  |  Branch (12932:33): [True: 329, False: 806]
  ------------------
12933|    329|              url.is_valid = false;
12934|    329|              return url;
12935|    329|            }
12936|    900|            state = state::HOST;
12937|    900|            break;
12938|  1.22k|          }
12939|  7.43k|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (12939:15): [True: 0, False: 7.43k]
  ------------------
12940|      0|            if constexpr (store_values) {
12941|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (12941:19): [True: 0, False: 0]
  ------------------
12942|      0|                url.update_unencoded_base_hash(*fragment);
12943|      0|              }
12944|      0|            }
12945|      0|            enforce_max_input_length();
12946|      0|            return url;
12947|      0|          }
12948|  7.43k|          input_position = end_of_authority + 1;
12949|  7.43k|        } while (true);
  ------------------
  |  Branch (12949:18): [True: 7.43k, Folded]
  ------------------
12950|       |
12951|    900|        break;
12952|  1.22k|      }
12953|    900|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (12953:7): [True: 280, False: 157k]
  ------------------
12954|    280|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
12955|    280|                helpers::substring(url_data, input_position));
12956|       |
12957|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
12958|       |        // then set state to special authority ignore slashes state and increase
12959|       |        // pointer by 1.
12960|    280|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (12960:13): [True: 203, False: 77]
  ------------------
12961|    203|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
12962|    203|          input_position += 2;
12963|    203|        } else {
12964|       |          // Otherwise, validation error, set state to relative state and
12965|       |          // decrease pointer by 1.
12966|     77|          state = state::RELATIVE_SCHEME;
12967|     77|        }
12968|       |
12969|    280|        break;
12970|  1.22k|      }
12971|  2.21k|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (12971:7): [True: 2.21k, False: 156k]
  ------------------
12972|  2.21k|        ada_log("PATH_OR_AUTHORITY ",
12973|  2.21k|                helpers::substring(url_data, input_position));
12974|       |
12975|       |        // If c is U+002F (/), then set state to authority state.
12976|  2.21k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (12976:13): [True: 2.10k, False: 111]
  ------------------
12977|  2.10k|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (12977:13): [True: 890, False: 1.21k]
  ------------------
12978|    890|          state = state::AUTHORITY;
12979|    890|          input_position++;
12980|  1.32k|        } else {
12981|       |          // Otherwise, set state to path state, and decrease pointer by 1.
12982|  1.32k|          state = state::PATH;
12983|  1.32k|        }
12984|       |
12985|  2.21k|        break;
12986|  1.22k|      }
12987|    574|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (12987:7): [True: 574, False: 157k]
  ------------------
12988|    574|        ada_log("RELATIVE_SCHEME ",
12989|    574|                helpers::substring(url_data, input_position));
12990|       |
12991|       |        // Set url's scheme to base's scheme.
12992|    574|        url.copy_scheme(*base_url);
12993|       |
12994|       |        // If c is U+002F (/), then set state to relative slash state.
12995|    574|        if ((input_position != input_size) &&
  ------------------
  |  Branch (12995:13): [True: 427, False: 147]
  ------------------
12996|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
12997|    427|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (12997:13): [True: 55, False: 372]
  ------------------
12998|     55|          ada_log(
12999|     55|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
13000|     55|              "slash state");
13001|     55|          state = state::RELATIVE_SLASH;
13002|    519|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13002:20): [True: 389, False: 130]
  |  Branch (13002:40): [True: 280, False: 109]
  ------------------
13003|    280|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13003:20): [True: 5, False: 275]
  ------------------
13004|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
13005|       |          // set state to relative slash state.
13006|      5|          ada_log(
13007|      5|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
13008|      5|              "error, set state to relative slash state");
13009|      5|          state = state::RELATIVE_SLASH;
13010|    514|        } else {
13011|    514|          ada_log("RELATIVE_SCHEME otherwise");
13012|       |          // Set url's username to base's username, url's password to base's
13013|       |          // password, url's host to base's host, url's port to base's port,
13014|       |          // url's path to a clone of base's path, and url's query to base's
13015|       |          // query.
13016|    514|          if constexpr (result_type_is_ada_url) {
13017|    514|            url.username = base_url->username;
13018|    514|            url.password = base_url->password;
13019|    514|            url.host = base_url->host;
13020|    514|            url.port = base_url->port;
13021|       |            // cloning the base path includes cloning the has_opaque_path flag
13022|    514|            url.has_opaque_path = base_url->has_opaque_path;
13023|    514|            url.path = base_url->path;
13024|    514|            url.query = base_url->query;
13025|       |          } else {
13026|       |            url.update_base_authority(base_url->get_href(),
13027|       |                                      base_url->get_components());
13028|       |            url.update_host_to_base_host(base_url->get_hostname());
13029|       |            url.update_base_port(base_url->retrieve_base_port());
13030|       |            // cloning the base path includes cloning the has_opaque_path flag
13031|       |            url.has_opaque_path = base_url->has_opaque_path;
13032|       |            url.update_base_pathname(base_url->get_pathname());
13033|       |            if (base_url->has_search()) {
13034|       |              // get_search() returns "" for an empty query string (URL ends
13035|       |              // with '?'). update_base_search("") would incorrectly clear the
13036|       |              // query, so pass "?" to preserve the empty query distinction.
13037|       |              auto s = base_url->get_search();
13038|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
13039|       |            }
13040|       |          }
13041|       |
13042|    514|          url.has_opaque_path = base_url->has_opaque_path;
13043|       |
13044|       |          // If c is U+003F (?), then set url's query to the empty string, and
13045|       |          // state to query state.
13046|    514|          if ((input_position != input_size) &&
  ------------------
  |  Branch (13046:15): [True: 367, False: 147]
  ------------------
13047|    367|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13047:15): [True: 17, False: 350]
  ------------------
13048|     17|            state = state::QUERY;
13049|     17|          }
13050|       |          // Otherwise, if c is not the EOF code point:
13051|    497|          else if (input_position != input_size) {
  ------------------
  |  Branch (13051:20): [True: 350, False: 147]
  ------------------
13052|       |            // Set url's query to null.
13053|    350|            url.clear_search();
13054|    350|            if constexpr (result_type_is_ada_url) {
13055|       |              // Shorten url's path.
13056|    350|              helpers::shorten_path(url.path, url.type);
13057|       |            } else {
13058|       |              std::string_view path = url.get_pathname();
13059|       |              if (helpers::shorten_path(path, url.type)) {
13060|       |                url.update_base_pathname(std::move(std::string(path)));
13061|       |              }
13062|       |            }
13063|       |            // Set state to path state and decrease pointer by 1.
13064|    350|            state = state::PATH;
13065|    350|            break;
13066|    350|          }
13067|    514|        }
13068|    224|        input_position++;
13069|    224|        break;
13070|    574|      }
13071|     60|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (13071:7): [True: 60, False: 158k]
  ------------------
13072|     60|        ada_log("RELATIVE_SLASH ",
13073|     60|                helpers::substring(url_data, input_position));
13074|       |
13075|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
13076|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13077|     60|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13077:13): [True: 44, False: 16]
  |  Branch (13077:33): [True: 33, False: 11]
  ------------------
13078|     33|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13078:14): [True: 9, False: 24]
  ------------------
13079|     24|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13079:14): [True: 2, False: 22]
  ------------------
13080|       |          // Set state to special authority ignore slashes state.
13081|     11|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13082|     11|        }
13083|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
13084|     49|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13084:18): [True: 32, False: 17]
  ------------------
13085|     32|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13085:18): [True: 6, False: 26]
  ------------------
13086|      6|          state = state::AUTHORITY;
13087|      6|        }
13088|       |        // Otherwise, set
13089|       |        // - url's username to base's username,
13090|       |        // - url's password to base's password,
13091|       |        // - url's host to base's host,
13092|       |        // - url's port to base's port,
13093|       |        // - state to path state, and then, decrease pointer by 1.
13094|     43|        else {
13095|     43|          if constexpr (result_type_is_ada_url) {
13096|     43|            url.username = base_url->username;
13097|     43|            url.password = base_url->password;
13098|     43|            url.host = base_url->host;
13099|     43|            url.port = base_url->port;
13100|       |          } else {
13101|       |            url.update_base_authority(base_url->get_href(),
13102|       |                                      base_url->get_components());
13103|       |            url.update_host_to_base_host(base_url->get_hostname());
13104|       |            url.update_base_port(base_url->retrieve_base_port());
13105|       |          }
13106|     43|          state = state::PATH;
13107|     43|          break;
13108|     43|        }
13109|       |
13110|     17|        input_position++;
13111|     17|        break;
13112|     60|      }
13113|  12.2k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (13113:7): [True: 12.2k, False: 146k]
  ------------------
13114|  12.2k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
13115|  12.2k|                helpers::substring(url_data, input_position));
13116|       |
13117|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13118|       |        // then set state to special authority ignore slashes state and increase
13119|       |        // pointer by 1.
13120|  12.2k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13120:13): [True: 3.14k, False: 9.10k]
  ------------------
13121|  3.14k|          input_position += 2;
13122|  3.14k|        }
13123|       |
13124|  12.2k|        [[fallthrough]];
13125|  12.2k|      }
13126|  12.4k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (13126:7): [True: 214, False: 158k]
  ------------------
13127|  12.4k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
13128|  12.4k|                helpers::substring(url_data, input_position));
13129|       |
13130|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
13131|       |        // authority state and decrease pointer by 1.
13132|  13.5k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13132:16): [True: 13.4k, False: 86]
  ------------------
13133|  13.4k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (13133:17): [True: 645, False: 12.7k]
  ------------------
13134|  12.7k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (13134:17): [True: 398, False: 12.3k]
  ------------------
13135|  1.04k|          input_position++;
13136|  1.04k|        }
13137|  12.4k|        state = state::AUTHORITY;
13138|       |
13139|  12.4k|        break;
13140|  12.2k|      }
13141|    993|      case state::QUERY: {
  ------------------
  |  Branch (13141:7): [True: 993, False: 157k]
  ------------------
13142|    993|        ada_log("QUERY ", helpers::substring(url_data, input_position));
13143|    993|        if constexpr (store_values) {
13144|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
13145|       |          // if url is special; otherwise the query percent-encode set.
13146|    993|          const uint8_t* query_percent_encode_set =
13147|    993|              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (13147:15): [True: 576, False: 417]
  ------------------
13148|    993|                               : character_sets::QUERY_PERCENT_ENCODE;
13149|       |
13150|       |          // Percent-encode after encoding, with encoding, buffer, and
13151|       |          // queryPercentEncodeSet, and append the result to url's query.
13152|    993|          url.update_base_search(url_data.substr(input_position),
13153|    993|                                 query_percent_encode_set);
13154|    993|          ada_log("QUERY update_base_search completed ");
13155|    993|          if (fragment.has_value()) {
  ------------------
  |  Branch (13155:15): [True: 211, False: 782]
  ------------------
13156|    211|            url.update_unencoded_base_hash(*fragment);
13157|    211|          }
13158|    993|        }
13159|    993|        enforce_max_input_length();
13160|    993|        return url;
13161|  12.2k|      }
13162|  13.0k|      case state::HOST: {
  ------------------
  |  Branch (13162:7): [True: 13.0k, False: 145k]
  ------------------
13163|  13.0k|        ada_log("HOST ", helpers::substring(url_data, input_position));
13164|       |
13165|  13.0k|        std::string_view host_view = url_data.substr(input_position);
13166|  13.0k|        auto [location, found_colon] =
13167|  13.0k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
13168|  13.0k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (13168:26): [True: 13.0k, False: 0]
  ------------------
13169|  13.0k|                             ? input_position + location
13170|  13.0k|                             : input_size;
13171|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
13172|       |        // Note: the 'found_colon' value is true if and only if a colon was
13173|       |        // encountered while not inside brackets.
13174|  13.0k|        if (found_colon) {
  ------------------
  |  Branch (13174:13): [True: 2.12k, False: 10.9k]
  ------------------
13175|       |          // If buffer is the empty string, validation error, return failure.
13176|       |          // Let host be the result of host parsing buffer with url is not
13177|       |          // special.
13178|  2.12k|          ada_log("HOST parsing ", host_view);
13179|  2.12k|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13179:15): [True: 227, False: 1.89k]
  ------------------
13180|    227|            return url;
13181|    227|          }
13182|  1.89k|          ada_log("HOST parsing results in ", url.get_hostname());
13183|       |          // Set url's host to host, buffer to the empty string, and state to
13184|       |          // port state.
13185|  1.89k|          state = state::PORT;
13186|  1.89k|          input_position++;
13187|  1.89k|        }
13188|       |        // Otherwise, if one of the following is true:
13189|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13190|       |        // - url is special and c is U+005C (\)
13191|       |        // The get_host_delimiter_location function either brings us to
13192|       |        // the colon outside of the bracket, or to one of those characters.
13193|  10.9k|        else {
13194|       |          // If url is special and host_view is the empty string, validation
13195|       |          // error, return failure.
13196|  10.9k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (13196:15): [True: 221, False: 10.6k]
  |  Branch (13196:36): [True: 91, False: 130]
  ------------------
13197|     91|            url.is_valid = false;
13198|     91|            return url;
13199|     91|          }
13200|  10.8k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
13201|       |          // Let host be the result of host parsing host_view with url is not
13202|       |          // special.
13203|  10.8k|          if (host_view.empty()) {
  ------------------
  |  Branch (13203:15): [True: 130, False: 10.6k]
  ------------------
13204|    130|            url.update_base_hostname("");
13205|  10.6k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13205:22): [True: 2.50k, False: 8.18k]
  ------------------
13206|  2.50k|            return url;
13207|  2.50k|          }
13208|  8.31k|          ada_log("HOST parsing results in ", url.get_hostname(),
13209|  8.31k|                  " href=", url.get_href());
13210|       |
13211|       |          // Set url's host to host, and state to path start state.
13212|  8.31k|          state = state::PATH_START;
13213|  8.31k|        }
13214|       |
13215|  10.2k|        break;
13216|  13.0k|      }
13217|  10.2k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (13217:7): [True: 3.33k, False: 154k]
  ------------------
13218|  3.33k|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
13219|       |        // Opaque path, query, and fragment are structurally always valid:
13220|       |        // the parser would just percent-encode whatever is there. When we
13221|       |        // are not storing values (can_parse), we can return immediately.
13222|       |        // We must set has_opaque_path = true before returning so that when
13223|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
13224|       |        // correctly rejects relative inputs against an opaque-path base
13225|       |        // (e.g. can_parse("", &"W:") must return false).
13226|       |        if constexpr (!store_values) {
13227|       |          url.has_opaque_path = true;
13228|       |          return url;
13229|       |        }
13230|  3.33k|        std::string_view view = url_data.substr(input_position);
13231|       |        // If c is U+003F (?), then set url's query to the empty string and
13232|       |        // state to query state.
13233|  3.33k|        size_t location = view.find('?');
13234|  3.33k|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (13234:13): [True: 304, False: 3.03k]
  ------------------
13235|    304|          view.remove_suffix(view.size() - location);
13236|    304|          state = state::QUERY;
13237|    304|          input_position += location + 1;
13238|  3.03k|        } else {
13239|  3.03k|          input_position = input_size + 1;
13240|  3.03k|        }
13241|  3.33k|        url.has_opaque_path = true;
13242|       |
13243|       |        // This is a really unlikely scenario in real world. We should not seek
13244|       |        // to optimize it.
13245|  3.33k|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (13245:13): [True: 117, False: 3.21k]
  ------------------
13246|    117|          std::string modified_view =
13247|    117|              std::string(view.substr(0, view.size() - 1)) + "%20";
13248|    117|          url.update_base_pathname(unicode::percent_encode(
13249|    117|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13250|  3.21k|        } else {
13251|  3.21k|          url.update_base_pathname(unicode::percent_encode(
13252|  3.21k|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13253|  3.21k|        }
13254|  3.33k|        break;
13255|  13.0k|      }
13256|  1.89k|      case state::PORT: {
  ------------------
  |  Branch (13256:7): [True: 1.89k, False: 156k]
  ------------------
13257|  1.89k|        ada_log("PORT ", helpers::substring(url_data, input_position));
13258|  1.89k|        std::string_view port_view = url_data.substr(input_position);
13259|  1.89k|        input_position += url.parse_port(port_view, true);
13260|  1.89k|        if (!url.is_valid) {
  ------------------
  |  Branch (13260:13): [True: 99, False: 1.79k]
  ------------------
13261|     99|          return url;
13262|     99|        }
13263|  1.79k|        state = state::PATH_START;
13264|  1.79k|        [[fallthrough]];
13265|  1.79k|      }
13266|  11.9k|      case state::PATH_START: {
  ------------------
  |  Branch (13266:7): [True: 10.1k, False: 148k]
  ------------------
13267|  11.9k|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
13268|       |        // Path, query, and fragment are structurally always valid: the
13269|       |        // parser would just percent-encode whatever is there. When we are
13270|       |        // not storing values (can_parse), we can return immediately since
13271|       |        // no subsequent state can invalidate the URL.
13272|       |        if constexpr (!store_values) {
13273|       |          return url;
13274|       |        }
13275|       |
13276|       |        // If url is special, then:
13277|  11.9k|        if (url.is_special()) {
  ------------------
  |  Branch (13277:13): [True: 11.2k, False: 777]
  ------------------
13278|       |          // Set state to path state.
13279|  11.2k|          state = state::PATH;
13280|       |
13281|       |          // Optimization: Avoiding going into PATH state improves the
13282|       |          // performance of urls ending with /.
13283|  11.2k|          if (input_position == input_size) {
  ------------------
  |  Branch (13283:15): [True: 5.94k, False: 5.27k]
  ------------------
13284|  5.94k|            if constexpr (store_values) {
13285|  5.94k|              url.update_base_pathname("/");
13286|  5.94k|              if (fragment.has_value()) {
  ------------------
  |  Branch (13286:19): [True: 117, False: 5.82k]
  ------------------
13287|    117|                url.update_unencoded_base_hash(*fragment);
13288|    117|              }
13289|  5.94k|            }
13290|  5.94k|            enforce_max_input_length();
13291|  5.94k|            return url;
13292|  5.94k|          }
13293|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
13294|       |          // by 1. We know that (input_position == input_size) is impossible
13295|       |          // here, because of the previous if-check.
13296|  5.27k|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (13296:15): [True: 323, False: 4.95k]
  ------------------
13297|    323|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (13297:15): [True: 310, False: 13]
  ------------------
13298|    310|            break;
13299|    310|          }
13300|  5.27k|        }
13301|       |        // Otherwise, if state override is not given and c is U+003F (?),
13302|       |        // set url's query to the empty string and state to query state.
13303|    777|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13303:18): [True: 165, False: 612]
  ------------------
13304|    165|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13304:18): [True: 56, False: 109]
  ------------------
13305|     56|          state = state::QUERY;
13306|     56|        }
13307|       |        // Otherwise, if c is not the EOF code point:
13308|    721|        else if (input_position != input_size) {
  ------------------
  |  Branch (13308:18): [True: 109, False: 612]
  ------------------
13309|       |          // Set state to path state.
13310|    109|          state = state::PATH;
13311|       |
13312|       |          // If c is not U+002F (/), then decrease pointer by 1.
13313|    109|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (13313:15): [True: 0, False: 109]
  ------------------
13314|      0|            break;
13315|      0|          }
13316|    109|        }
13317|       |
13318|  5.74k|        input_position++;
13319|  5.74k|        break;
13320|  11.9k|      }
13321|  7.81k|      case state::PATH: {
  ------------------
  |  Branch (13321:7): [True: 7.81k, False: 150k]
  ------------------
13322|  7.81k|        ada_log("PATH ", helpers::substring(url_data, input_position));
13323|       |        // Path, query, and fragment are structurally always valid: the
13324|       |        // parser would just percent-encode whatever is there. When we are
13325|       |        // not storing values (can_parse), we can return immediately since
13326|       |        // no subsequent state can invalidate the URL.
13327|       |        if constexpr (!store_values) {
13328|       |          return url;
13329|       |        }
13330|  7.81k|        std::string_view view = url_data.substr(input_position);
13331|       |
13332|       |        // Most time, we do not need percent encoding.
13333|       |        // Furthermore, we can immediately locate the '?'.
13334|  7.81k|        size_t locofquestionmark = view.find('?');
13335|  7.81k|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (13335:13): [True: 612, False: 7.20k]
  ------------------
13336|    612|          state = state::QUERY;
13337|    612|          view.remove_suffix(view.size() - locofquestionmark);
13338|    612|          input_position += locofquestionmark + 1;
13339|  7.20k|        } else {
13340|  7.20k|          input_position = input_size + 1;
13341|  7.20k|        }
13342|  7.81k|        if constexpr (store_values) {
13343|  7.81k|          if constexpr (result_type_is_ada_url) {
13344|  7.81k|            helpers::parse_prepared_path(view, url.type, url.path);
13345|       |          } else {
13346|       |            url.consume_prepared_path(view);
13347|       |            ADA_ASSERT_TRUE(url.validate());
13348|       |          }
13349|  7.81k|        }
13350|  7.81k|        break;
13351|  11.9k|      }
13352|  2.21k|      case state::FILE_SLASH: {
  ------------------
  |  Branch (13352:7): [True: 2.21k, False: 156k]
  ------------------
13353|  2.21k|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
13354|       |
13355|       |        // If c is U+002F (/) or U+005C (\), then:
13356|  2.21k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13356:13): [True: 2.15k, False: 58]
  ------------------
13357|  2.15k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13357:14): [True: 2.10k, False: 52]
  ------------------
13358|  2.10k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13358:14): [True: 6, False: 46]
  ------------------
13359|  2.10k|          ada_log("FILE_SLASH c is U+002F or U+005C");
13360|       |          // Set state to file host state.
13361|  2.10k|          state = state::FILE_HOST;
13362|  2.10k|          input_position++;
13363|  2.10k|        } else {
13364|    104|          ada_log("FILE_SLASH otherwise");
13365|       |          // If base is non-null and base's scheme is "file", then:
13366|       |          // Note: it is unsafe to do base_url->scheme unless you know that
13367|       |          // base_url_has_value() is true.
13368|    104|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13368:15): [True: 81, False: 23]
  |  Branch (13368:38): [True: 78, False: 3]
  ------------------
13369|       |            // Set url's host to base's host.
13370|     78|            if constexpr (result_type_is_ada_url) {
13371|     78|              url.host = base_url->host;
13372|       |            } else {
13373|       |              url.update_host_to_base_host(base_url->get_host());
13374|       |            }
13375|       |            // If the code point substring from pointer to the end of input does
13376|       |            // not start with a Windows drive letter and base's path[0] is a
13377|       |            // normalized Windows drive letter, then append base's path[0] to
13378|       |            // url's path.
13379|     78|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (13379:17): [True: 78, False: 0]
  ------------------
13380|     78|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (13380:19): [True: 72, False: 6]
  ------------------
13381|     78|                      url_data.substr(input_position))) {
13382|     72|                std::string_view first_base_url_path =
13383|     72|                    base_url->get_pathname().substr(1);
13384|     72|                size_t loc = first_base_url_path.find('/');
13385|     72|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (13385:21): [True: 10, False: 62]
  ------------------
13386|     10|                  helpers::resize(first_base_url_path, loc);
13387|     10|                }
13388|     72|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (13388:21): [True: 4, False: 68]
  ------------------
13389|     72|                        first_base_url_path)) {
13390|      4|                  if constexpr (result_type_is_ada_url) {
13391|      4|                    url.path += '/';
13392|      4|                    url.path += first_base_url_path;
13393|       |                  } else {
13394|       |                    url.append_base_pathname(
13395|       |                        helpers::concat("/", first_base_url_path));
13396|       |                  }
13397|      4|                }
13398|     72|              }
13399|     78|            }
13400|     78|          }
13401|       |
13402|       |          // Set state to path state, and decrease pointer by 1.
13403|    104|          state = state::PATH;
13404|    104|        }
13405|       |
13406|  2.21k|        break;
13407|  11.9k|      }
13408|  2.10k|      case state::FILE_HOST: {
  ------------------
  |  Branch (13408:7): [True: 2.10k, False: 156k]
  ------------------
13409|  2.10k|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
13410|  2.10k|        std::string_view view = url_data.substr(input_position);
13411|       |
13412|  2.10k|        size_t location = view.find_first_of("/\\?");
13413|  2.10k|        std::string_view file_host_buffer = view.substr(
13414|  2.10k|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (13414:16): [True: 898, False: 1.21k]
  ------------------
13415|       |
13416|  2.10k|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (13416:13): [True: 12, False: 2.09k]
  ------------------
13417|     12|          state = state::PATH;
13418|  2.09k|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (13418:20): [True: 284, False: 1.81k]
  ------------------
13419|       |          // Set url's host to the empty string.
13420|    284|          if constexpr (result_type_is_ada_url) {
13421|    284|            url.host = "";
13422|       |          } else {
13423|       |            url.update_base_hostname("");
13424|       |          }
13425|       |          // Set state to path start state.
13426|    284|          state = state::PATH_START;
13427|  1.81k|        } else {
13428|  1.81k|          size_t consumed_bytes = file_host_buffer.size();
13429|  1.81k|          input_position += consumed_bytes;
13430|       |          // Let host be the result of host parsing buffer with url is not
13431|       |          // special.
13432|  1.81k|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (13432:15): [True: 213, False: 1.60k]
  ------------------
13433|    213|            return url;
13434|    213|          }
13435|       |
13436|  1.60k|          if constexpr (result_type_is_ada_url) {
13437|       |            // If host is "localhost", then set host to the empty string.
13438|  1.60k|            if (url.host.has_value() && url.host.value() == "localhost") {
  ------------------
  |  Branch (13438:17): [True: 1.60k, False: 0]
  |  Branch (13438:41): [True: 6, False: 1.59k]
  ------------------
13439|      6|              url.host = "";
13440|      6|            }
13441|       |          } else {
13442|       |            if (url.get_hostname() == "localhost") {
13443|       |              url.update_base_hostname("");
13444|       |            }
13445|       |          }
13446|       |
13447|       |          // Set buffer to the empty string and state to path start state.
13448|  1.60k|          state = state::PATH_START;
13449|  1.60k|        }
13450|       |
13451|  1.89k|        break;
13452|  2.10k|      }
13453|  2.87k|      case state::FILE: {
  ------------------
  |  Branch (13453:7): [True: 2.87k, False: 155k]
  ------------------
13454|  2.87k|        ada_log("FILE ", helpers::substring(url_data, input_position));
13455|  2.87k|        std::string_view file_view = url_data.substr(input_position);
13456|       |
13457|  2.87k|        url.set_protocol_as_file();
13458|  2.87k|        if constexpr (result_type_is_ada_url) {
13459|       |          // Set url's host to the empty string.
13460|  2.87k|          url.host = "";
13461|       |        } else {
13462|       |          url.update_base_hostname("");
13463|       |        }
13464|       |        // If c is U+002F (/) or U+005C (\), then:
13465|  2.87k|        if (input_position != input_size &&
  ------------------
  |  Branch (13465:13): [True: 2.74k, False: 125]
  ------------------
13466|  2.74k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13466:14): [True: 2.20k, False: 539]
  ------------------
13467|  2.21k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13467:14): [True: 7, False: 532]
  ------------------
13468|  2.21k|          ada_log("FILE c is U+002F or U+005C");
13469|       |          // Set state to file slash state.
13470|  2.21k|          state = state::FILE_SLASH;
13471|  2.21k|        }
13472|       |        // Otherwise, if base is non-null and base's scheme is "file":
13473|    657|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13473:18): [True: 242, False: 415]
  |  Branch (13473:41): [True: 166, False: 76]
  ------------------
13474|       |          // Set url's host to base's host, url's path to a clone of base's
13475|       |          // path, and url's query to base's query.
13476|    166|          ada_log("FILE base non-null");
13477|    166|          if constexpr (result_type_is_ada_url) {
13478|    166|            url.host = base_url->host;
13479|    166|            url.path = base_url->path;
13480|    166|            url.query = base_url->query;
13481|       |          } else {
13482|       |            url.update_host_to_base_host(base_url->get_hostname());
13483|       |            url.update_base_pathname(base_url->get_pathname());
13484|       |            if (base_url->has_search()) {
13485|       |              // get_search() returns "" for an empty query string (URL ends
13486|       |              // with '?'). update_base_search("") would incorrectly clear the
13487|       |              // query, so pass "?" to preserve the empty query distinction.
13488|       |              auto s = base_url->get_search();
13489|       |              url.update_base_search(s.empty() ? std::string_view("?") : s);
13490|       |            }
13491|       |          }
13492|    166|          url.has_opaque_path = base_url->has_opaque_path;
13493|       |
13494|       |          // If c is U+003F (?), then set url's query to the empty string and
13495|       |          // state to query state.
13496|    166|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (13496:15): [True: 115, False: 51]
  |  Branch (13496:47): [True: 4, False: 111]
  ------------------
13497|      4|            state = state::QUERY;
13498|      4|          }
13499|       |          // Otherwise, if c is not the EOF code point:
13500|    162|          else if (input_position != input_size) {
  ------------------
  |  Branch (13500:20): [True: 111, False: 51]
  ------------------
13501|       |            // Set url's query to null.
13502|    111|            url.clear_search();
13503|       |            // If the code point substring from pointer to the end of input does
13504|       |            // not start with a Windows drive letter, then shorten url's path.
13505|    111|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (13505:17): [True: 92, False: 19]
  ------------------
13506|     92|              if constexpr (result_type_is_ada_url) {
13507|     92|                helpers::shorten_path(url.path, url.type);
13508|       |              } else {
13509|       |                std::string_view path = url.get_pathname();
13510|       |                if (helpers::shorten_path(path, url.type)) {
13511|       |                  url.update_base_pathname(std::move(std::string(path)));
13512|       |                }
13513|       |              }
13514|     92|            }
13515|       |            // Otherwise:
13516|     19|            else {
13517|       |              // Set url's path to an empty list.
13518|     19|              url.clear_pathname();
13519|     19|              url.has_opaque_path = true;
13520|     19|            }
13521|       |
13522|       |            // Set state to path state and decrease pointer by 1.
13523|    111|            state = state::PATH;
13524|    111|            break;
13525|    111|          }
13526|    166|        }
13527|       |        // Otherwise, set state to path state, and decrease pointer by 1.
13528|    491|        else {
13529|    491|          ada_log("FILE go to path");
13530|    491|          state = state::PATH;
13531|    491|          break;
13532|    491|        }
13533|       |
13534|  2.26k|        input_position++;
13535|  2.26k|        break;
13536|  2.87k|      }
13537|      0|      default:
  ------------------
  |  Branch (13537:7): [True: 0, False: 158k]
  ------------------
13538|      0|        unreachable();
13539|   158k|    }
13540|   158k|  }
13541|  15.4k|  if constexpr (store_values) {
13542|  15.4k|    if (fragment.has_value()) {
  ------------------
  |  Branch (13542:9): [True: 355, False: 15.0k]
  ------------------
13543|    355|      url.update_unencoded_base_hash(*fragment);
13544|    355|    }
13545|  15.4k|  }
13546|  15.4k|  enforce_max_input_length();
13547|  15.4k|  return url;
13548|  46.5k|}
_ZN3ada6parser14parse_url_implINS_14url_aggregatorELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
12528|  46.5k|                           const result_type* base_url) {
12529|       |  // We can specialize the implementation per type.
12530|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
12531|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
12532|       |  // something else } is free (at runtime). This means that ada::url_aggregator
12533|       |  // and ada::url **do not have to support the exact same API**.
12534|  46.5k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
12535|  46.5k|  constexpr bool result_type_is_ada_url_aggregator =
12536|  46.5k|      std::is_same_v<url_aggregator, result_type>;
12537|  46.5k|  static_assert(result_type_is_ada_url ||
12538|  46.5k|                result_type_is_ada_url_aggregator);  // We don't support
12539|       |                                                     // anything else for now.
12540|       |
12541|  46.5k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
12542|  46.5k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
12543|  46.5k|          ")");
12544|       |
12545|  46.5k|  result_type url{};
12546|       |
12547|  46.5k|  const uint32_t max_input_length = ada::get_max_input_length();
12548|       |
12549|       |  // Normalization (percent-encoding, IDNA) can push the serialized URL past
12550|       |  // max_input_length even when the input fit under it. The check at the end of
12551|       |  // the state machine covers URLs that run to completion, but several states
12552|       |  // return early after appending a query or fragment, so run the same check on
12553|       |  // those exits too. Without this, a query- or fragment-terminated URL such as
12554|       |  // file://x/?<...> or https://user@x/?<...> is accepted while the equivalent
12555|       |  // https://x/?<...> that takes the absolute fast path is rejected.
12556|  46.5k|  const auto enforce_max_input_length = [&]() {
12557|  46.5k|    if constexpr (store_values) {
12558|  46.5k|      if (url.is_valid) {
12559|  46.5k|        if constexpr (result_type_is_ada_url_aggregator) {
12560|  46.5k|          if (url.buffer.size() > max_input_length) [[unlikely]] {
12561|  46.5k|            url.is_valid = false;
12562|  46.5k|          }
12563|  46.5k|        } else {
12564|  46.5k|          if (url.get_href_size() > max_input_length) [[unlikely]] {
12565|  46.5k|            url.is_valid = false;
12566|  46.5k|          }
12567|  46.5k|        }
12568|  46.5k|      }
12569|  46.5k|    }
12570|  46.5k|  };
12571|       |
12572|       |  // We refuse to parse URL strings that exceed the maximum input length.
12573|       |  // By default, this is 4GB but can be configured via
12574|       |  // ada::set_max_input_length().
12575|  46.5k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12575:7): [True: 0, False: 46.5k]
  ------------------
12576|      0|    url.is_valid = false;
12577|      0|  }
12578|       |  // Going forward, user_input.size() is in [0,
12579|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
12580|       |  // base, or the optional_url was invalid, we must return.
12581|  46.5k|  if (base_url != nullptr) {
  ------------------
  |  Branch (12581:7): [True: 3.93k, False: 42.6k]
  ------------------
12582|  3.93k|    url.is_valid &= base_url->is_valid;
12583|  3.93k|  }
12584|  46.5k|  if (!url.is_valid) {
  ------------------
  |  Branch (12584:7): [True: 0, False: 46.5k]
  ------------------
12585|      0|    return url;
12586|      0|  }
12587|       |
12588|       |  // Simple absolute / relative fast paths (before tabs/newline scan).
12589|  46.5k|  if constexpr (store_values) {
12590|  46.5k|    bool hit_fast_path = false;
12591|  46.5k|    if (base_url == nullptr) {
  ------------------
  |  Branch (12591:9): [True: 42.6k, False: 3.93k]
  ------------------
12592|       |      // IPv4/IPv6 and userinfo miss the fast path. Skip the never_inline
12593|       |      // call so those URLs (including SetHref) do not pay for a miss.
12594|  42.6k|      const auto* p = reinterpret_cast<const uint8_t*>(user_input.data());
12595|  42.6k|      const size_t n = user_input.size();
12596|  42.6k|      size_t host_start = 0;
12597|  42.6k|      if (n >= 8 && p[4] == ':' && p[5] == '/' && p[6] == '/') {
  ------------------
  |  Branch (12597:11): [True: 16.3k, False: 26.2k]
  |  Branch (12597:21): [True: 3.02k, False: 13.3k]
  |  Branch (12597:36): [True: 2.30k, False: 723]
  |  Branch (12597:51): [True: 2.28k, False: 24]
  ------------------
12598|  2.28k|        host_start = 7;
12599|  40.3k|      } else if (n >= 9 && p[5] == ':' && p[6] == '/' && p[7] == '/') {
  ------------------
  |  Branch (12599:18): [True: 12.7k, False: 27.5k]
  |  Branch (12599:28): [True: 240, False: 12.4k]
  |  Branch (12599:43): [True: 43, False: 197]
  |  Branch (12599:58): [True: 27, False: 16]
  ------------------
12600|     27|        host_start = 8;
12601|     27|      }
12602|  42.6k|      const uint8_t host_first = host_start != 0 ? p[host_start] : 0;
  ------------------
  |  Branch (12602:34): [True: 2.30k, False: 40.3k]
  ------------------
12603|  42.6k|      const bool skip_ip =
12604|  42.6k|          host_first == '[' || (host_first >= '0' && host_first <= '9');
  ------------------
  |  Branch (12604:11): [True: 34, False: 42.5k]
  |  Branch (12604:33): [True: 1.66k, False: 40.9k]
  |  Branch (12604:54): [True: 563, False: 1.09k]
  ------------------
12605|  42.6k|      const bool skip_userinfo =
12606|  42.6k|          host_start != 0 && authority_has_at(p, host_start, n);
  ------------------
  |  Branch (12606:11): [True: 2.30k, False: 40.3k]
  |  Branch (12606:30): [True: 22, False: 2.28k]
  ------------------
12607|  42.6k|      hit_fast_path = !skip_ip && !skip_userinfo &&
  ------------------
  |  Branch (12607:23): [True: 42.0k, False: 597]
  |  Branch (12607:35): [True: 41.9k, False: 22]
  ------------------
12608|  41.9k|                      try_parse_simple_absolute(user_input, url);
  ------------------
  |  Branch (12608:23): [True: 4.23k, False: 37.7k]
  ------------------
12609|  42.6k|    } else {
12610|  3.93k|      hit_fast_path = try_parse_simple_relative(user_input, *base_url, url);
12611|  3.93k|    }
12612|  46.5k|    if (hit_fast_path) {
  ------------------
  |  Branch (12612:9): [True: 4.37k, False: 42.1k]
  ------------------
12613|  4.37k|      if constexpr (result_type_is_ada_url_aggregator) {
12614|  4.37k|        if (url.buffer.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12614:13): [True: 0, False: 4.37k]
  ------------------
12615|      0|          url.is_valid = false;
12616|      0|        }
12617|       |      } else {
12618|       |        // For a small absolute input, even worst-case normalization cannot
12619|       |        // exceed the limit: percent encoding expands at most 3x and IDNA at
12620|       |        // most 4.5x. Avoid traversing every stored component on the common
12621|       |        // default-limit path.
12622|       |        if ((base_url != nullptr ||
12623|       |             user_input.size() > static_cast<size_t>(max_input_length) / 5) &&
12624|       |            url.get_href_size() > max_input_length) [[unlikely]] {
12625|       |          url.is_valid = false;
12626|       |        }
12627|       |      }
12628|  4.37k|      return url;
12629|  4.37k|    }
12630|  46.5k|  }
12631|       |
12632|  42.1k|  state state = state::SCHEME_START;
12633|       |
12634|  46.5k|  std::string tmp_buffer;
12635|  46.5k|  std::string_view url_data;
12636|  46.5k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (12636:7): [True: 296, False: 46.2k]
  ------------------
12637|    296|    tmp_buffer = user_input;
12638|       |    // Optimization opportunity: Instead of copying and then pruning, we could
12639|       |    // just directly build the string from user_input.
12640|    296|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
12641|    296|    url_data = tmp_buffer;
12642|  46.2k|  } else [[likely]] {
12643|  46.2k|    url_data = user_input;
12644|  46.2k|  }
12645|       |
12646|       |  // Leading and trailing control characters are uncommon and easy to deal with
12647|       |  // (no performance concern).
12648|  46.5k|  helpers::trim_c0_whitespace(url_data);
12649|       |
12650|  46.5k|  if constexpr (result_type_is_ada_url_aggregator && store_values) {
12651|       |    // Most of the time, we just need user_input.size().
12652|       |    // In some instances, we may need a bit more.
12653|       |    ///////////////////////////
12654|       |    // This is *very* important. This line should *not* be removed
12655|       |    // hastily. There are principled reasons why reserve is important
12656|       |    // for performance. If you have a benchmark with small inputs,
12657|       |    // it may not matter, but in other instances, it could.
12658|       |    ////
12659|       |    // This rounds up to the next power of two.
12660|       |    // We know that user_input.size() is in [0,
12661|       |    // std::numeric_limits<uint32_t>::max).
12662|  46.5k|    uint32_t reserve_capacity =
12663|  46.5k|        (0xFFFFFFFF >>
12664|  46.5k|         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
12665|  46.5k|        1;
12666|  46.5k|    url.reserve(reserve_capacity);
12667|  46.5k|  }
12668|       |
12669|       |  // Optimization opportunity. Most websites do not have fragment.
12670|  46.5k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
12671|       |  // We add it last so that an implementation like ada::url_aggregator
12672|       |  // can append it last to its internal buffer, thus improving performance.
12673|       |
12674|       |  // Here url_data no longer has its fragment.
12675|       |  // We are going to access the data from url_data (it is immutable).
12676|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
12677|       |  // The input_position variable should range from 0 to input_size.
12678|       |  // It is illegal to access url_data at input_size.
12679|  46.5k|  size_t input_position = 0;
12680|  46.5k|  const size_t input_size = url_data.size();
12681|       |  // Keep running the following state machine by switching on state.
12682|       |  // If after a run pointer points to the EOF code point, go to the next step.
12683|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
12684|       |  // We never decrement input_position.
12685|   173k|  while (input_position <= input_size) {
  ------------------
  |  Branch (12685:10): [True: 158k, False: 15.4k]
  ------------------
12686|   158k|    ada_log("In parsing at ", input_position, " out of ", input_size,
12687|   158k|            " in state ", ada::to_string(state));
12688|   158k|    switch (state) {
12689|  42.1k|      case state::SCHEME_START: {
  ------------------
  |  Branch (12689:7): [True: 42.1k, False: 116k]
  ------------------
12690|  42.1k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
12691|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
12692|       |        // state to scheme state.
12693|  42.1k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (12693:13): [True: 22.2k, False: 19.8k]
  ------------------
12694|  22.2k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (12694:13): [True: 21.2k, False: 998]
  ------------------
12695|  21.2k|          state = state::SCHEME;
12696|  21.2k|          input_position++;
12697|  21.2k|        } else {
12698|       |          // Otherwise, if state override is not given, set state to no scheme
12699|       |          // state and decrease pointer by 1.
12700|  20.8k|          state = state::NO_SCHEME;
12701|  20.8k|        }
12702|  42.1k|        break;
12703|      0|      }
12704|  21.2k|      case state::SCHEME: {
  ------------------
  |  Branch (12704:7): [True: 21.2k, False: 137k]
  ------------------
12705|  21.2k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
12706|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
12707|       |        // append c, lowercased, to buffer.
12708|  50.5k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (12708:16): [True: 50.2k, False: 295]
  ------------------
12709|  50.2k|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (12709:16): [True: 29.2k, False: 20.9k]
  ------------------
12710|  29.2k|          input_position++;
12711|  29.2k|        }
12712|       |        // Otherwise, if c is U+003A (:), then:
12713|  21.2k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (12713:13): [True: 20.9k, False: 295]
  ------------------
12714|  20.9k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (12714:13): [True: 20.7k, False: 255]
  ------------------
12715|  20.7k|          ada_log("SCHEME the scheme should be ",
12716|  20.7k|                  url_data.substr(0, input_position));
12717|       |          if constexpr (result_type_is_ada_url) {
12718|       |            if (!url.parse_scheme(url_data.substr(0, input_position))) {
12719|       |              return url;
12720|       |            }
12721|  20.7k|          } else {
12722|       |            // we pass the colon along instead of painfully adding it back.
12723|  20.7k|            if (!url.parse_scheme_with_colon(
  ------------------
  |  Branch (12723:17): [True: 0, False: 20.7k]
  ------------------
12724|  20.7k|                    url_data.substr(0, input_position + 1))) {
12725|      0|              return url;
12726|      0|            }
12727|  20.7k|          }
12728|  20.7k|          ada_log("SCHEME the scheme is ", url.get_protocol());
12729|       |
12730|       |          // If url's scheme is "file", then:
12731|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
12732|  20.7k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (12732:15): [True: 2.64k, False: 18.0k]
  ------------------
12733|       |            // Set state to file state.
12734|  2.64k|            state = state::FILE;
12735|  2.64k|          }
12736|       |          // Otherwise, if url is special, base is non-null, and base's scheme
12737|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
12738|       |          // != nullptr is false.
12739|  18.0k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (12739:20): [True: 12.5k, False: 5.54k]
  |  Branch (12739:40): [True: 2.12k, False: 10.4k]
  ------------------
12740|  2.12k|                   base_url->type == url.type) {
  ------------------
  |  Branch (12740:20): [True: 280, False: 1.84k]
  ------------------
12741|       |            // Set state to special relative or authority state.
12742|    280|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
12743|    280|          }
12744|       |          // Otherwise, if url is special, set state to special authority
12745|       |          // slashes state.
12746|  17.7k|          else if (url.is_special()) {
  ------------------
  |  Branch (12746:20): [True: 12.2k, False: 5.54k]
  ------------------
12747|  12.2k|            state = state::SPECIAL_AUTHORITY_SLASHES;
12748|  12.2k|          }
12749|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
12750|       |          // path or authority state and increase pointer by 1.
12751|  5.54k|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (12751:20): [True: 3.19k, False: 2.35k]
  ------------------
12752|  3.19k|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (12752:20): [True: 2.21k, False: 980]
  ------------------
12753|  2.21k|            state = state::PATH_OR_AUTHORITY;
12754|  2.21k|            input_position++;
12755|  2.21k|          }
12756|       |          // Otherwise, set url's path to the empty string and set state to
12757|       |          // opaque path state.
12758|  3.33k|          else {
12759|  3.33k|            state = state::OPAQUE_PATH;
12760|  3.33k|          }
12761|  20.7k|        }
12762|       |        // Otherwise, if state override is not given, set buffer to the empty
12763|       |        // string, state to no scheme state, and start over (from the first code
12764|       |        // point in input).
12765|    550|        else {
12766|    550|          state = state::NO_SCHEME;
12767|    550|          input_position = 0;
12768|    550|          break;
12769|    550|        }
12770|  20.7k|        input_position++;
12771|  20.7k|        break;
12772|  21.2k|      }
12773|  21.4k|      case state::NO_SCHEME: {
  ------------------
  |  Branch (12773:7): [True: 21.4k, False: 136k]
  ------------------
12774|  21.4k|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
12775|       |        // The fragment was pruned from url_data before the state machine ran,
12776|       |        // so 'c is U+0023 (#)' holds exactly when a fragment was found and
12777|       |        // nothing else remains in front of it.
12778|  21.4k|        const bool c_is_hash =
12779|  21.4k|            fragment.has_value() && input_position == input_size;
  ------------------
  |  Branch (12779:13): [True: 248, False: 21.1k]
  |  Branch (12779:37): [True: 196, False: 52]
  ------------------
12780|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
12781|       |        // validation error, return failure.
12782|  21.4k|        if (base_url == nullptr || (base_url->has_opaque_path && !c_is_hash)) {
  ------------------
  |  Branch (12782:13): [True: 20.6k, False: 838]
  |  Branch (12782:37): [True: 118, False: 720]
  |  Branch (12782:66): [True: 47, False: 71]
  ------------------
12783|  20.6k|          ada_log("NO_SCHEME validation error");
12784|  20.6k|          url.is_valid = false;
12785|  20.6k|          return url;
12786|  20.6k|        }
12787|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
12788|       |        // set url's scheme to base's scheme, url's path to base's path, url's
12789|       |        // query to base's query, and set state to fragment state.
12790|    791|        else if (base_url->has_opaque_path && c_is_hash) {
  ------------------
  |  Branch (12790:18): [True: 71, False: 720]
  |  Branch (12790:47): [True: 71, False: 0]
  ------------------
12791|     71|          ada_log("NO_SCHEME opaque base with fragment");
12792|     71|          url.copy_scheme(*base_url);
12793|     71|          url.has_opaque_path = base_url->has_opaque_path;
12794|       |
12795|       |          if constexpr (result_type_is_ada_url) {
12796|       |            url.path = base_url->path;
12797|       |            url.query = base_url->query;
12798|     71|          } else {
12799|     71|            url.update_base_pathname(base_url->get_pathname());
12800|     71|            if (base_url->has_search()) {
  ------------------
  |  Branch (12800:17): [True: 30, False: 41]
  ------------------
12801|       |              // get_search() returns "" for an empty query string (URL ends
12802|       |              // with '?'). update_base_search("") would incorrectly clear the
12803|       |              // query, so pass "?" to preserve the empty query distinction.
12804|     30|              auto s = base_url->get_search();
12805|     30|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (12805:38): [True: 13, False: 17]
  ------------------
12806|     30|            }
12807|     71|          }
12808|     71|          url.update_unencoded_base_hash(*fragment);
12809|     71|          enforce_max_input_length();
12810|     71|          return url;
12811|     71|        }
12812|       |        // Otherwise, if base's scheme is not "file", set state to relative
12813|       |        // state and decrease pointer by 1.
12814|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
12815|    720|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (12815:18): [True: 497, False: 223]
  ------------------
12816|    497|          ada_log("NO_SCHEME non-file relative path");
12817|    497|          state = state::RELATIVE_SCHEME;
12818|    497|        }
12819|       |        // Otherwise, set state to file state and decrease pointer by 1.
12820|    223|        else {
12821|    223|          ada_log("NO_SCHEME file base type");
12822|    223|          state = state::FILE;
12823|    223|        }
12824|    720|        break;
12825|  21.4k|      }
12826|  13.3k|      case state::AUTHORITY: {
  ------------------
  |  Branch (12826:7): [True: 13.3k, False: 144k]
  ------------------
12827|  13.3k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
12828|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
12829|       |        // about AUTHORITY. Of course, we could have @ and still not have to
12830|       |        // worry about AUTHORITY.
12831|       |        // TODO: Instead of just collecting a bool, collect the location of the
12832|       |        // '@' and do something useful with it.
12833|       |        // TODO: We could do various processing early on, using a single pass
12834|       |        // over the string to collect information about it, e.g., telling us
12835|       |        // whether there is a @ and if so, where (or how many).
12836|       |
12837|       |        // Check if url data contains an @.
12838|  13.3k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (12838:13): [True: 12.1k, False: 1.22k]
  ------------------
12839|  12.1k|          state = state::HOST;
12840|  12.1k|          break;
12841|  12.1k|        }
12842|  1.22k|        bool at_sign_seen{false};
12843|  1.22k|        bool password_token_seen{false};
12844|       |        /**
12845|       |         * We expect something of the sort...
12846|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
12847|       |         * --------^
12848|       |         */
12849|  8.66k|        do {
12850|  8.66k|          std::string_view view = url_data.substr(input_position);
12851|       |          // The delimiters are @, /, ? \\.
12852|  8.66k|          size_t location =
12853|  8.66k|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (12853:15): [True: 7.70k, False: 957]
  ------------------
12854|  8.66k|                               : helpers::find_authority_delimiter(view);
12855|  8.66k|          std::string_view authority_view = view.substr(0, location);
12856|  8.66k|          size_t end_of_authority = input_position + authority_view.size();
12857|       |          // If c is U+0040 (@), then:
12858|  8.66k|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (12858:15): [True: 7.85k, False: 813]
  ------------------
12859|  7.85k|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (12859:15): [True: 7.43k, False: 416]
  ------------------
12860|       |            // If atSignSeen is true, then prepend "%40" to buffer.
12861|  7.43k|            if (at_sign_seen) {
  ------------------
  |  Branch (12861:17): [True: 6.30k, False: 1.13k]
  ------------------
12862|  6.30k|              if (password_token_seen) {
  ------------------
  |  Branch (12862:19): [True: 2.49k, False: 3.81k]
  ------------------
12863|       |                if constexpr (result_type_is_ada_url) {
12864|       |                  url.password += "%40";
12865|  2.49k|                } else {
12866|  2.49k|                  url.append_base_password("%40");
12867|  2.49k|                }
12868|  3.81k|              } else {
12869|       |                if constexpr (result_type_is_ada_url) {
12870|       |                  url.username += "%40";
12871|  3.81k|                } else {
12872|  3.81k|                  url.append_base_username("%40");
12873|  3.81k|                }
12874|  3.81k|              }
12875|  6.30k|            }
12876|       |
12877|  7.43k|            at_sign_seen = true;
12878|       |
12879|  7.43k|            if (!password_token_seen) {
  ------------------
  |  Branch (12879:17): [True: 4.94k, False: 2.49k]
  ------------------
12880|  4.94k|              size_t password_token_location = authority_view.find(':');
12881|  4.94k|              password_token_seen =
12882|  4.94k|                  password_token_location != std::string_view::npos;
12883|       |
12884|  4.94k|              if constexpr (store_values) {
12885|  4.94k|                if (!password_token_seen) {
  ------------------
  |  Branch (12885:21): [True: 4.53k, False: 406]
  ------------------
12886|       |                  if constexpr (result_type_is_ada_url) {
12887|       |                    url.username += unicode::percent_encode(
12888|       |                        authority_view,
12889|       |                        character_sets::USERINFO_PERCENT_ENCODE);
12890|  4.53k|                  } else {
12891|  4.53k|                    url.append_base_username(unicode::percent_encode(
12892|  4.53k|                        authority_view,
12893|  4.53k|                        character_sets::USERINFO_PERCENT_ENCODE));
12894|  4.53k|                  }
12895|  4.53k|                } else {
12896|       |                  if constexpr (result_type_is_ada_url) {
12897|       |                    url.username += unicode::percent_encode(
12898|       |                        authority_view.substr(0, password_token_location),
12899|       |                        character_sets::USERINFO_PERCENT_ENCODE);
12900|       |                    url.password += unicode::percent_encode(
12901|       |                        authority_view.substr(password_token_location + 1),
12902|       |                        character_sets::USERINFO_PERCENT_ENCODE);
12903|    406|                  } else {
12904|    406|                    url.append_base_username(unicode::percent_encode(
12905|    406|                        authority_view.substr(0, password_token_location),
12906|    406|                        character_sets::USERINFO_PERCENT_ENCODE));
12907|    406|                    url.append_base_password(unicode::percent_encode(
12908|    406|                        authority_view.substr(password_token_location + 1),
12909|    406|                        character_sets::USERINFO_PERCENT_ENCODE));
12910|    406|                  }
12911|    406|                }
12912|  4.94k|              }
12913|  4.94k|            } else if constexpr (store_values) {
12914|       |              if constexpr (result_type_is_ada_url) {
12915|       |                url.password += unicode::percent_encode(
12916|       |                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
12917|  2.49k|              } else {
12918|  2.49k|                url.append_base_password(unicode::percent_encode(
12919|  2.49k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
12920|  2.49k|              }
12921|  2.49k|            }
12922|  7.43k|          }
12923|       |          // Otherwise, if one of the following is true:
12924|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
12925|       |          // - url is special and c is U+005C (\)
12926|  1.22k|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (12926:20): [True: 813, False: 416]
  ------------------
12927|    416|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (12927:20): [True: 376, False: 40]
  ------------------
12928|     40|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (12928:20): [True: 34, False: 6]
  ------------------
12929|  1.22k|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (12929:21): [True: 6, False: 0]
  |  Branch (12929:41): [True: 6, False: 0]
  ------------------
12930|       |            // If atSignSeen is true and authority_view is the empty string,
12931|       |            // validation error, return failure.
12932|  1.22k|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (12932:17): [True: 1.13k, False: 94]
  |  Branch (12932:33): [True: 329, False: 806]
  ------------------
12933|    329|              url.is_valid = false;
12934|    329|              return url;
12935|    329|            }
12936|    900|            state = state::HOST;
12937|    900|            break;
12938|  1.22k|          }
12939|  7.43k|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (12939:15): [True: 0, False: 7.43k]
  ------------------
12940|      0|            if constexpr (store_values) {
12941|      0|              if (fragment.has_value()) {
  ------------------
  |  Branch (12941:19): [True: 0, False: 0]
  ------------------
12942|      0|                url.update_unencoded_base_hash(*fragment);
12943|      0|              }
12944|      0|            }
12945|      0|            enforce_max_input_length();
12946|      0|            return url;
12947|      0|          }
12948|  7.43k|          input_position = end_of_authority + 1;
12949|  7.43k|        } while (true);
  ------------------
  |  Branch (12949:18): [True: 7.43k, Folded]
  ------------------
12950|       |
12951|    900|        break;
12952|  1.22k|      }
12953|    900|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (12953:7): [True: 280, False: 157k]
  ------------------
12954|    280|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
12955|    280|                helpers::substring(url_data, input_position));
12956|       |
12957|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
12958|       |        // then set state to special authority ignore slashes state and increase
12959|       |        // pointer by 1.
12960|    280|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (12960:13): [True: 203, False: 77]
  ------------------
12961|    203|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
12962|    203|          input_position += 2;
12963|    203|        } else {
12964|       |          // Otherwise, validation error, set state to relative state and
12965|       |          // decrease pointer by 1.
12966|     77|          state = state::RELATIVE_SCHEME;
12967|     77|        }
12968|       |
12969|    280|        break;
12970|  1.22k|      }
12971|  2.21k|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (12971:7): [True: 2.21k, False: 156k]
  ------------------
12972|  2.21k|        ada_log("PATH_OR_AUTHORITY ",
12973|  2.21k|                helpers::substring(url_data, input_position));
12974|       |
12975|       |        // If c is U+002F (/), then set state to authority state.
12976|  2.21k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (12976:13): [True: 2.10k, False: 111]
  ------------------
12977|  2.10k|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (12977:13): [True: 890, False: 1.21k]
  ------------------
12978|    890|          state = state::AUTHORITY;
12979|    890|          input_position++;
12980|  1.32k|        } else {
12981|       |          // Otherwise, set state to path state, and decrease pointer by 1.
12982|  1.32k|          state = state::PATH;
12983|  1.32k|        }
12984|       |
12985|  2.21k|        break;
12986|  1.22k|      }
12987|    574|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (12987:7): [True: 574, False: 157k]
  ------------------
12988|    574|        ada_log("RELATIVE_SCHEME ",
12989|    574|                helpers::substring(url_data, input_position));
12990|       |
12991|       |        // Set url's scheme to base's scheme.
12992|    574|        url.copy_scheme(*base_url);
12993|       |
12994|       |        // If c is U+002F (/), then set state to relative slash state.
12995|    574|        if ((input_position != input_size) &&
  ------------------
  |  Branch (12995:13): [True: 427, False: 147]
  ------------------
12996|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
12997|    427|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (12997:13): [True: 55, False: 372]
  ------------------
12998|     55|          ada_log(
12999|     55|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
13000|     55|              "slash state");
13001|     55|          state = state::RELATIVE_SLASH;
13002|    519|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13002:20): [True: 389, False: 130]
  |  Branch (13002:40): [True: 280, False: 109]
  ------------------
13003|    280|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13003:20): [True: 5, False: 275]
  ------------------
13004|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
13005|       |          // set state to relative slash state.
13006|      5|          ada_log(
13007|      5|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
13008|      5|              "error, set state to relative slash state");
13009|      5|          state = state::RELATIVE_SLASH;
13010|    514|        } else {
13011|    514|          ada_log("RELATIVE_SCHEME otherwise");
13012|       |          // Set url's username to base's username, url's password to base's
13013|       |          // password, url's host to base's host, url's port to base's port,
13014|       |          // url's path to a clone of base's path, and url's query to base's
13015|       |          // query.
13016|       |          if constexpr (result_type_is_ada_url) {
13017|       |            url.username = base_url->username;
13018|       |            url.password = base_url->password;
13019|       |            url.host = base_url->host;
13020|       |            url.port = base_url->port;
13021|       |            // cloning the base path includes cloning the has_opaque_path flag
13022|       |            url.has_opaque_path = base_url->has_opaque_path;
13023|       |            url.path = base_url->path;
13024|       |            url.query = base_url->query;
13025|    514|          } else {
13026|    514|            url.update_base_authority(base_url->get_href(),
13027|    514|                                      base_url->get_components());
13028|    514|            url.update_host_to_base_host(base_url->get_hostname());
13029|    514|            url.update_base_port(base_url->retrieve_base_port());
13030|       |            // cloning the base path includes cloning the has_opaque_path flag
13031|    514|            url.has_opaque_path = base_url->has_opaque_path;
13032|    514|            url.update_base_pathname(base_url->get_pathname());
13033|    514|            if (base_url->has_search()) {
  ------------------
  |  Branch (13033:17): [True: 64, False: 450]
  ------------------
13034|       |              // get_search() returns "" for an empty query string (URL ends
13035|       |              // with '?'). update_base_search("") would incorrectly clear the
13036|       |              // query, so pass "?" to preserve the empty query distinction.
13037|     64|              auto s = base_url->get_search();
13038|     64|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13038:38): [True: 30, False: 34]
  ------------------
13039|     64|            }
13040|    514|          }
13041|       |
13042|    514|          url.has_opaque_path = base_url->has_opaque_path;
13043|       |
13044|       |          // If c is U+003F (?), then set url's query to the empty string, and
13045|       |          // state to query state.
13046|    514|          if ((input_position != input_size) &&
  ------------------
  |  Branch (13046:15): [True: 367, False: 147]
  ------------------
13047|    367|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13047:15): [True: 17, False: 350]
  ------------------
13048|     17|            state = state::QUERY;
13049|     17|          }
13050|       |          // Otherwise, if c is not the EOF code point:
13051|    497|          else if (input_position != input_size) {
  ------------------
  |  Branch (13051:20): [True: 350, False: 147]
  ------------------
13052|       |            // Set url's query to null.
13053|    350|            url.clear_search();
13054|       |            if constexpr (result_type_is_ada_url) {
13055|       |              // Shorten url's path.
13056|       |              helpers::shorten_path(url.path, url.type);
13057|    350|            } else {
13058|    350|              std::string_view path = url.get_pathname();
13059|    350|              if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (13059:19): [True: 347, False: 3]
  ------------------
13060|    347|                url.update_base_pathname(std::move(std::string(path)));
13061|    347|              }
13062|    350|            }
13063|       |            // Set state to path state and decrease pointer by 1.
13064|    350|            state = state::PATH;
13065|    350|            break;
13066|    350|          }
13067|    514|        }
13068|    224|        input_position++;
13069|    224|        break;
13070|    574|      }
13071|     60|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (13071:7): [True: 60, False: 158k]
  ------------------
13072|     60|        ada_log("RELATIVE_SLASH ",
13073|     60|                helpers::substring(url_data, input_position));
13074|       |
13075|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
13076|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13077|     60|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13077:13): [True: 44, False: 16]
  |  Branch (13077:33): [True: 33, False: 11]
  ------------------
13078|     33|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13078:14): [True: 9, False: 24]
  ------------------
13079|     24|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13079:14): [True: 2, False: 22]
  ------------------
13080|       |          // Set state to special authority ignore slashes state.
13081|     11|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13082|     11|        }
13083|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
13084|     49|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13084:18): [True: 32, False: 17]
  ------------------
13085|     32|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13085:18): [True: 6, False: 26]
  ------------------
13086|      6|          state = state::AUTHORITY;
13087|      6|        }
13088|       |        // Otherwise, set
13089|       |        // - url's username to base's username,
13090|       |        // - url's password to base's password,
13091|       |        // - url's host to base's host,
13092|       |        // - url's port to base's port,
13093|       |        // - state to path state, and then, decrease pointer by 1.
13094|     43|        else {
13095|       |          if constexpr (result_type_is_ada_url) {
13096|       |            url.username = base_url->username;
13097|       |            url.password = base_url->password;
13098|       |            url.host = base_url->host;
13099|       |            url.port = base_url->port;
13100|     43|          } else {
13101|     43|            url.update_base_authority(base_url->get_href(),
13102|     43|                                      base_url->get_components());
13103|     43|            url.update_host_to_base_host(base_url->get_hostname());
13104|     43|            url.update_base_port(base_url->retrieve_base_port());
13105|     43|          }
13106|     43|          state = state::PATH;
13107|     43|          break;
13108|     43|        }
13109|       |
13110|     17|        input_position++;
13111|     17|        break;
13112|     60|      }
13113|  12.2k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (13113:7): [True: 12.2k, False: 146k]
  ------------------
13114|  12.2k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
13115|  12.2k|                helpers::substring(url_data, input_position));
13116|       |
13117|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13118|       |        // then set state to special authority ignore slashes state and increase
13119|       |        // pointer by 1.
13120|  12.2k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13120:13): [True: 3.14k, False: 9.10k]
  ------------------
13121|  3.14k|          input_position += 2;
13122|  3.14k|        }
13123|       |
13124|  12.2k|        [[fallthrough]];
13125|  12.2k|      }
13126|  12.4k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (13126:7): [True: 214, False: 158k]
  ------------------
13127|  12.4k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
13128|  12.4k|                helpers::substring(url_data, input_position));
13129|       |
13130|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
13131|       |        // authority state and decrease pointer by 1.
13132|  13.5k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13132:16): [True: 13.4k, False: 86]
  ------------------
13133|  13.4k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (13133:17): [True: 645, False: 12.7k]
  ------------------
13134|  12.7k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (13134:17): [True: 398, False: 12.3k]
  ------------------
13135|  1.04k|          input_position++;
13136|  1.04k|        }
13137|  12.4k|        state = state::AUTHORITY;
13138|       |
13139|  12.4k|        break;
13140|  12.2k|      }
13141|    993|      case state::QUERY: {
  ------------------
  |  Branch (13141:7): [True: 993, False: 157k]
  ------------------
13142|    993|        ada_log("QUERY ", helpers::substring(url_data, input_position));
13143|    993|        if constexpr (store_values) {
13144|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
13145|       |          // if url is special; otherwise the query percent-encode set.
13146|    993|          const uint8_t* query_percent_encode_set =
13147|    993|              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
  ------------------
  |  Branch (13147:15): [True: 576, False: 417]
  ------------------
13148|    993|                               : character_sets::QUERY_PERCENT_ENCODE;
13149|       |
13150|       |          // Percent-encode after encoding, with encoding, buffer, and
13151|       |          // queryPercentEncodeSet, and append the result to url's query.
13152|    993|          url.update_base_search(url_data.substr(input_position),
13153|    993|                                 query_percent_encode_set);
13154|    993|          ada_log("QUERY update_base_search completed ");
13155|    993|          if (fragment.has_value()) {
  ------------------
  |  Branch (13155:15): [True: 211, False: 782]
  ------------------
13156|    211|            url.update_unencoded_base_hash(*fragment);
13157|    211|          }
13158|    993|        }
13159|    993|        enforce_max_input_length();
13160|    993|        return url;
13161|  12.2k|      }
13162|  13.0k|      case state::HOST: {
  ------------------
  |  Branch (13162:7): [True: 13.0k, False: 145k]
  ------------------
13163|  13.0k|        ada_log("HOST ", helpers::substring(url_data, input_position));
13164|       |
13165|  13.0k|        std::string_view host_view = url_data.substr(input_position);
13166|  13.0k|        auto [location, found_colon] =
13167|  13.0k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
13168|  13.0k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (13168:26): [True: 13.0k, False: 0]
  ------------------
13169|  13.0k|                             ? input_position + location
13170|  13.0k|                             : input_size;
13171|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
13172|       |        // Note: the 'found_colon' value is true if and only if a colon was
13173|       |        // encountered while not inside brackets.
13174|  13.0k|        if (found_colon) {
  ------------------
  |  Branch (13174:13): [True: 2.12k, False: 10.9k]
  ------------------
13175|       |          // If buffer is the empty string, validation error, return failure.
13176|       |          // Let host be the result of host parsing buffer with url is not
13177|       |          // special.
13178|  2.12k|          ada_log("HOST parsing ", host_view);
13179|  2.12k|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13179:15): [True: 227, False: 1.89k]
  ------------------
13180|    227|            return url;
13181|    227|          }
13182|  1.89k|          ada_log("HOST parsing results in ", url.get_hostname());
13183|       |          // Set url's host to host, buffer to the empty string, and state to
13184|       |          // port state.
13185|  1.89k|          state = state::PORT;
13186|  1.89k|          input_position++;
13187|  1.89k|        }
13188|       |        // Otherwise, if one of the following is true:
13189|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13190|       |        // - url is special and c is U+005C (\)
13191|       |        // The get_host_delimiter_location function either brings us to
13192|       |        // the colon outside of the bracket, or to one of those characters.
13193|  10.9k|        else {
13194|       |          // If url is special and host_view is the empty string, validation
13195|       |          // error, return failure.
13196|  10.9k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (13196:15): [True: 221, False: 10.6k]
  |  Branch (13196:36): [True: 91, False: 130]
  ------------------
13197|     91|            url.is_valid = false;
13198|     91|            return url;
13199|     91|          }
13200|  10.8k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
13201|       |          // Let host be the result of host parsing host_view with url is not
13202|       |          // special.
13203|  10.8k|          if (host_view.empty()) {
  ------------------
  |  Branch (13203:15): [True: 130, False: 10.6k]
  ------------------
13204|    130|            url.update_base_hostname("");
13205|  10.6k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13205:22): [True: 2.50k, False: 8.18k]
  ------------------
13206|  2.50k|            return url;
13207|  2.50k|          }
13208|  8.31k|          ada_log("HOST parsing results in ", url.get_hostname(),
13209|  8.31k|                  " href=", url.get_href());
13210|       |
13211|       |          // Set url's host to host, and state to path start state.
13212|  8.31k|          state = state::PATH_START;
13213|  8.31k|        }
13214|       |
13215|  10.2k|        break;
13216|  13.0k|      }
13217|  10.2k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (13217:7): [True: 3.33k, False: 154k]
  ------------------
13218|  3.33k|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
13219|       |        // Opaque path, query, and fragment are structurally always valid:
13220|       |        // the parser would just percent-encode whatever is there. When we
13221|       |        // are not storing values (can_parse), we can return immediately.
13222|       |        // We must set has_opaque_path = true before returning so that when
13223|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
13224|       |        // correctly rejects relative inputs against an opaque-path base
13225|       |        // (e.g. can_parse("", &"W:") must return false).
13226|       |        if constexpr (!store_values) {
13227|       |          url.has_opaque_path = true;
13228|       |          return url;
13229|       |        }
13230|  3.33k|        std::string_view view = url_data.substr(input_position);
13231|       |        // If c is U+003F (?), then set url's query to the empty string and
13232|       |        // state to query state.
13233|  3.33k|        size_t location = view.find('?');
13234|  3.33k|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (13234:13): [True: 304, False: 3.03k]
  ------------------
13235|    304|          view.remove_suffix(view.size() - location);
13236|    304|          state = state::QUERY;
13237|    304|          input_position += location + 1;
13238|  3.03k|        } else {
13239|  3.03k|          input_position = input_size + 1;
13240|  3.03k|        }
13241|  3.33k|        url.has_opaque_path = true;
13242|       |
13243|       |        // This is a really unlikely scenario in real world. We should not seek
13244|       |        // to optimize it.
13245|  3.33k|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (13245:13): [True: 117, False: 3.21k]
  ------------------
13246|    117|          std::string modified_view =
13247|    117|              std::string(view.substr(0, view.size() - 1)) + "%20";
13248|    117|          url.update_base_pathname(unicode::percent_encode(
13249|    117|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13250|  3.21k|        } else {
13251|  3.21k|          url.update_base_pathname(unicode::percent_encode(
13252|  3.21k|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13253|  3.21k|        }
13254|  3.33k|        break;
13255|  13.0k|      }
13256|  1.89k|      case state::PORT: {
  ------------------
  |  Branch (13256:7): [True: 1.89k, False: 156k]
  ------------------
13257|  1.89k|        ada_log("PORT ", helpers::substring(url_data, input_position));
13258|  1.89k|        std::string_view port_view = url_data.substr(input_position);
13259|  1.89k|        input_position += url.parse_port(port_view, true);
13260|  1.89k|        if (!url.is_valid) {
  ------------------
  |  Branch (13260:13): [True: 99, False: 1.79k]
  ------------------
13261|     99|          return url;
13262|     99|        }
13263|  1.79k|        state = state::PATH_START;
13264|  1.79k|        [[fallthrough]];
13265|  1.79k|      }
13266|  11.9k|      case state::PATH_START: {
  ------------------
  |  Branch (13266:7): [True: 10.1k, False: 148k]
  ------------------
13267|  11.9k|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
13268|       |        // Path, query, and fragment are structurally always valid: the
13269|       |        // parser would just percent-encode whatever is there. When we are
13270|       |        // not storing values (can_parse), we can return immediately since
13271|       |        // no subsequent state can invalidate the URL.
13272|       |        if constexpr (!store_values) {
13273|       |          return url;
13274|       |        }
13275|       |
13276|       |        // If url is special, then:
13277|  11.9k|        if (url.is_special()) {
  ------------------
  |  Branch (13277:13): [True: 11.2k, False: 777]
  ------------------
13278|       |          // Set state to path state.
13279|  11.2k|          state = state::PATH;
13280|       |
13281|       |          // Optimization: Avoiding going into PATH state improves the
13282|       |          // performance of urls ending with /.
13283|  11.2k|          if (input_position == input_size) {
  ------------------
  |  Branch (13283:15): [True: 5.94k, False: 5.27k]
  ------------------
13284|  5.94k|            if constexpr (store_values) {
13285|  5.94k|              url.update_base_pathname("/");
13286|  5.94k|              if (fragment.has_value()) {
  ------------------
  |  Branch (13286:19): [True: 117, False: 5.82k]
  ------------------
13287|    117|                url.update_unencoded_base_hash(*fragment);
13288|    117|              }
13289|  5.94k|            }
13290|  5.94k|            enforce_max_input_length();
13291|  5.94k|            return url;
13292|  5.94k|          }
13293|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
13294|       |          // by 1. We know that (input_position == input_size) is impossible
13295|       |          // here, because of the previous if-check.
13296|  5.27k|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (13296:15): [True: 323, False: 4.95k]
  ------------------
13297|    323|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (13297:15): [True: 310, False: 13]
  ------------------
13298|    310|            break;
13299|    310|          }
13300|  5.27k|        }
13301|       |        // Otherwise, if state override is not given and c is U+003F (?),
13302|       |        // set url's query to the empty string and state to query state.
13303|    777|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13303:18): [True: 165, False: 612]
  ------------------
13304|    165|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13304:18): [True: 56, False: 109]
  ------------------
13305|     56|          state = state::QUERY;
13306|     56|        }
13307|       |        // Otherwise, if c is not the EOF code point:
13308|    721|        else if (input_position != input_size) {
  ------------------
  |  Branch (13308:18): [True: 109, False: 612]
  ------------------
13309|       |          // Set state to path state.
13310|    109|          state = state::PATH;
13311|       |
13312|       |          // If c is not U+002F (/), then decrease pointer by 1.
13313|    109|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (13313:15): [True: 0, False: 109]
  ------------------
13314|      0|            break;
13315|      0|          }
13316|    109|        }
13317|       |
13318|  5.74k|        input_position++;
13319|  5.74k|        break;
13320|  11.9k|      }
13321|  7.81k|      case state::PATH: {
  ------------------
  |  Branch (13321:7): [True: 7.81k, False: 150k]
  ------------------
13322|  7.81k|        ada_log("PATH ", helpers::substring(url_data, input_position));
13323|       |        // Path, query, and fragment are structurally always valid: the
13324|       |        // parser would just percent-encode whatever is there. When we are
13325|       |        // not storing values (can_parse), we can return immediately since
13326|       |        // no subsequent state can invalidate the URL.
13327|       |        if constexpr (!store_values) {
13328|       |          return url;
13329|       |        }
13330|  7.81k|        std::string_view view = url_data.substr(input_position);
13331|       |
13332|       |        // Most time, we do not need percent encoding.
13333|       |        // Furthermore, we can immediately locate the '?'.
13334|  7.81k|        size_t locofquestionmark = view.find('?');
13335|  7.81k|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (13335:13): [True: 612, False: 7.20k]
  ------------------
13336|    612|          state = state::QUERY;
13337|    612|          view.remove_suffix(view.size() - locofquestionmark);
13338|    612|          input_position += locofquestionmark + 1;
13339|  7.20k|        } else {
13340|  7.20k|          input_position = input_size + 1;
13341|  7.20k|        }
13342|  7.81k|        if constexpr (store_values) {
13343|       |          if constexpr (result_type_is_ada_url) {
13344|       |            helpers::parse_prepared_path(view, url.type, url.path);
13345|  7.81k|          } else {
13346|  7.81k|            url.consume_prepared_path(view);
13347|  7.81k|            ADA_ASSERT_TRUE(url.validate());
13348|  7.81k|          }
13349|  7.81k|        }
13350|  7.81k|        break;
13351|  11.9k|      }
13352|  2.21k|      case state::FILE_SLASH: {
  ------------------
  |  Branch (13352:7): [True: 2.21k, False: 156k]
  ------------------
13353|  2.21k|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
13354|       |
13355|       |        // If c is U+002F (/) or U+005C (\), then:
13356|  2.21k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13356:13): [True: 2.15k, False: 58]
  ------------------
13357|  2.15k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13357:14): [True: 2.10k, False: 52]
  ------------------
13358|  2.10k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13358:14): [True: 6, False: 46]
  ------------------
13359|  2.10k|          ada_log("FILE_SLASH c is U+002F or U+005C");
13360|       |          // Set state to file host state.
13361|  2.10k|          state = state::FILE_HOST;
13362|  2.10k|          input_position++;
13363|  2.10k|        } else {
13364|    104|          ada_log("FILE_SLASH otherwise");
13365|       |          // If base is non-null and base's scheme is "file", then:
13366|       |          // Note: it is unsafe to do base_url->scheme unless you know that
13367|       |          // base_url_has_value() is true.
13368|    104|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13368:15): [True: 81, False: 23]
  |  Branch (13368:38): [True: 78, False: 3]
  ------------------
13369|       |            // Set url's host to base's host.
13370|       |            if constexpr (result_type_is_ada_url) {
13371|       |              url.host = base_url->host;
13372|     78|            } else {
13373|     78|              url.update_host_to_base_host(base_url->get_host());
13374|     78|            }
13375|       |            // If the code point substring from pointer to the end of input does
13376|       |            // not start with a Windows drive letter and base's path[0] is a
13377|       |            // normalized Windows drive letter, then append base's path[0] to
13378|       |            // url's path.
13379|     78|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (13379:17): [True: 78, False: 0]
  ------------------
13380|     78|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (13380:19): [True: 72, False: 6]
  ------------------
13381|     78|                      url_data.substr(input_position))) {
13382|     72|                std::string_view first_base_url_path =
13383|     72|                    base_url->get_pathname().substr(1);
13384|     72|                size_t loc = first_base_url_path.find('/');
13385|     72|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (13385:21): [True: 10, False: 62]
  ------------------
13386|     10|                  helpers::resize(first_base_url_path, loc);
13387|     10|                }
13388|     72|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (13388:21): [True: 4, False: 68]
  ------------------
13389|     72|                        first_base_url_path)) {
13390|       |                  if constexpr (result_type_is_ada_url) {
13391|       |                    url.path += '/';
13392|       |                    url.path += first_base_url_path;
13393|      4|                  } else {
13394|      4|                    url.append_base_pathname(
13395|      4|                        helpers::concat("/", first_base_url_path));
13396|      4|                  }
13397|      4|                }
13398|     72|              }
13399|     78|            }
13400|     78|          }
13401|       |
13402|       |          // Set state to path state, and decrease pointer by 1.
13403|    104|          state = state::PATH;
13404|    104|        }
13405|       |
13406|  2.21k|        break;
13407|  11.9k|      }
13408|  2.10k|      case state::FILE_HOST: {
  ------------------
  |  Branch (13408:7): [True: 2.10k, False: 156k]
  ------------------
13409|  2.10k|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
13410|  2.10k|        std::string_view view = url_data.substr(input_position);
13411|       |
13412|  2.10k|        size_t location = view.find_first_of("/\\?");
13413|  2.10k|        std::string_view file_host_buffer = view.substr(
13414|  2.10k|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (13414:16): [True: 898, False: 1.21k]
  ------------------
13415|       |
13416|  2.10k|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (13416:13): [True: 12, False: 2.09k]
  ------------------
13417|     12|          state = state::PATH;
13418|  2.09k|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (13418:20): [True: 284, False: 1.81k]
  ------------------
13419|       |          // Set url's host to the empty string.
13420|       |          if constexpr (result_type_is_ada_url) {
13421|       |            url.host = "";
13422|    284|          } else {
13423|    284|            url.update_base_hostname("");
13424|    284|          }
13425|       |          // Set state to path start state.
13426|    284|          state = state::PATH_START;
13427|  1.81k|        } else {
13428|  1.81k|          size_t consumed_bytes = file_host_buffer.size();
13429|  1.81k|          input_position += consumed_bytes;
13430|       |          // Let host be the result of host parsing buffer with url is not
13431|       |          // special.
13432|  1.81k|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (13432:15): [True: 213, False: 1.60k]
  ------------------
13433|    213|            return url;
13434|    213|          }
13435|       |
13436|       |          if constexpr (result_type_is_ada_url) {
13437|       |            // If host is "localhost", then set host to the empty string.
13438|       |            if (url.host.has_value() && url.host.value() == "localhost") {
13439|       |              url.host = "";
13440|       |            }
13441|  1.60k|          } else {
13442|  1.60k|            if (url.get_hostname() == "localhost") {
  ------------------
  |  Branch (13442:17): [True: 6, False: 1.59k]
  ------------------
13443|      6|              url.update_base_hostname("");
13444|      6|            }
13445|  1.60k|          }
13446|       |
13447|       |          // Set buffer to the empty string and state to path start state.
13448|  1.60k|          state = state::PATH_START;
13449|  1.60k|        }
13450|       |
13451|  1.89k|        break;
13452|  2.10k|      }
13453|  2.87k|      case state::FILE: {
  ------------------
  |  Branch (13453:7): [True: 2.87k, False: 155k]
  ------------------
13454|  2.87k|        ada_log("FILE ", helpers::substring(url_data, input_position));
13455|  2.87k|        std::string_view file_view = url_data.substr(input_position);
13456|       |
13457|  2.87k|        url.set_protocol_as_file();
13458|       |        if constexpr (result_type_is_ada_url) {
13459|       |          // Set url's host to the empty string.
13460|       |          url.host = "";
13461|  2.87k|        } else {
13462|  2.87k|          url.update_base_hostname("");
13463|  2.87k|        }
13464|       |        // If c is U+002F (/) or U+005C (\), then:
13465|  2.87k|        if (input_position != input_size &&
  ------------------
  |  Branch (13465:13): [True: 2.74k, False: 125]
  ------------------
13466|  2.74k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13466:14): [True: 2.20k, False: 539]
  ------------------
13467|  2.21k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13467:14): [True: 7, False: 532]
  ------------------
13468|  2.21k|          ada_log("FILE c is U+002F or U+005C");
13469|       |          // Set state to file slash state.
13470|  2.21k|          state = state::FILE_SLASH;
13471|  2.21k|        }
13472|       |        // Otherwise, if base is non-null and base's scheme is "file":
13473|    657|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13473:18): [True: 242, False: 415]
  |  Branch (13473:41): [True: 166, False: 76]
  ------------------
13474|       |          // Set url's host to base's host, url's path to a clone of base's
13475|       |          // path, and url's query to base's query.
13476|    166|          ada_log("FILE base non-null");
13477|       |          if constexpr (result_type_is_ada_url) {
13478|       |            url.host = base_url->host;
13479|       |            url.path = base_url->path;
13480|       |            url.query = base_url->query;
13481|    166|          } else {
13482|    166|            url.update_host_to_base_host(base_url->get_hostname());
13483|    166|            url.update_base_pathname(base_url->get_pathname());
13484|    166|            if (base_url->has_search()) {
  ------------------
  |  Branch (13484:17): [True: 58, False: 108]
  ------------------
13485|       |              // get_search() returns "" for an empty query string (URL ends
13486|       |              // with '?'). update_base_search("") would incorrectly clear the
13487|       |              // query, so pass "?" to preserve the empty query distinction.
13488|     58|              auto s = base_url->get_search();
13489|     58|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13489:38): [True: 19, False: 39]
  ------------------
13490|     58|            }
13491|    166|          }
13492|    166|          url.has_opaque_path = base_url->has_opaque_path;
13493|       |
13494|       |          // If c is U+003F (?), then set url's query to the empty string and
13495|       |          // state to query state.
13496|    166|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (13496:15): [True: 115, False: 51]
  |  Branch (13496:47): [True: 4, False: 111]
  ------------------
13497|      4|            state = state::QUERY;
13498|      4|          }
13499|       |          // Otherwise, if c is not the EOF code point:
13500|    162|          else if (input_position != input_size) {
  ------------------
  |  Branch (13500:20): [True: 111, False: 51]
  ------------------
13501|       |            // Set url's query to null.
13502|    111|            url.clear_search();
13503|       |            // If the code point substring from pointer to the end of input does
13504|       |            // not start with a Windows drive letter, then shorten url's path.
13505|    111|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (13505:17): [True: 92, False: 19]
  ------------------
13506|       |              if constexpr (result_type_is_ada_url) {
13507|       |                helpers::shorten_path(url.path, url.type);
13508|     92|              } else {
13509|     92|                std::string_view path = url.get_pathname();
13510|     92|                if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (13510:21): [True: 90, False: 2]
  ------------------
13511|     90|                  url.update_base_pathname(std::move(std::string(path)));
13512|     90|                }
13513|     92|              }
13514|     92|            }
13515|       |            // Otherwise:
13516|     19|            else {
13517|       |              // Set url's path to an empty list.
13518|     19|              url.clear_pathname();
13519|     19|              url.has_opaque_path = true;
13520|     19|            }
13521|       |
13522|       |            // Set state to path state and decrease pointer by 1.
13523|    111|            state = state::PATH;
13524|    111|            break;
13525|    111|          }
13526|    166|        }
13527|       |        // Otherwise, set state to path state, and decrease pointer by 1.
13528|    491|        else {
13529|    491|          ada_log("FILE go to path");
13530|    491|          state = state::PATH;
13531|    491|          break;
13532|    491|        }
13533|       |
13534|  2.26k|        input_position++;
13535|  2.26k|        break;
13536|  2.87k|      }
13537|      0|      default:
  ------------------
  |  Branch (13537:7): [True: 0, False: 158k]
  ------------------
13538|      0|        unreachable();
13539|   158k|    }
13540|   158k|  }
13541|  15.4k|  if constexpr (store_values) {
13542|  15.4k|    if (fragment.has_value()) {
  ------------------
  |  Branch (13542:9): [True: 355, False: 15.0k]
  ------------------
13543|    355|      url.update_unencoded_base_hash(*fragment);
13544|    355|    }
13545|  15.4k|  }
13546|  15.4k|  enforce_max_input_length();
13547|  15.4k|  return url;
13548|  46.5k|}
_ZN3ada6parser14parse_url_implINS_14url_aggregatorELb0EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_:
12528|  29.3k|                           const result_type* base_url) {
12529|       |  // We can specialize the implementation per type.
12530|       |  // Important: result_type_is_ada_url is evaluated at *compile time*. This
12531|       |  // means that doing if constexpr(result_type_is_ada_url) { something } else {
12532|       |  // something else } is free (at runtime). This means that ada::url_aggregator
12533|       |  // and ada::url **do not have to support the exact same API**.
12534|  29.3k|  constexpr bool result_type_is_ada_url = std::is_same_v<url, result_type>;
12535|  29.3k|  constexpr bool result_type_is_ada_url_aggregator =
12536|  29.3k|      std::is_same_v<url_aggregator, result_type>;
12537|  29.3k|  static_assert(result_type_is_ada_url ||
12538|  29.3k|                result_type_is_ada_url_aggregator);  // We don't support
12539|       |                                                     // anything else for now.
12540|       |
12541|  29.3k|  ada_log("ada::parser::parse_url('", user_input, "' [", user_input.size(),
12542|  29.3k|          " bytes],", (base_url != nullptr ? base_url->to_string() : "null"),
12543|  29.3k|          ")");
12544|       |
12545|  29.3k|  result_type url{};
12546|       |
12547|  29.3k|  const uint32_t max_input_length = ada::get_max_input_length();
12548|       |
12549|       |  // Normalization (percent-encoding, IDNA) can push the serialized URL past
12550|       |  // max_input_length even when the input fit under it. The check at the end of
12551|       |  // the state machine covers URLs that run to completion, but several states
12552|       |  // return early after appending a query or fragment, so run the same check on
12553|       |  // those exits too. Without this, a query- or fragment-terminated URL such as
12554|       |  // file://x/?<...> or https://user@x/?<...> is accepted while the equivalent
12555|       |  // https://x/?<...> that takes the absolute fast path is rejected.
12556|  29.3k|  const auto enforce_max_input_length = [&]() {
12557|  29.3k|    if constexpr (store_values) {
12558|  29.3k|      if (url.is_valid) {
12559|  29.3k|        if constexpr (result_type_is_ada_url_aggregator) {
12560|  29.3k|          if (url.buffer.size() > max_input_length) [[unlikely]] {
12561|  29.3k|            url.is_valid = false;
12562|  29.3k|          }
12563|  29.3k|        } else {
12564|  29.3k|          if (url.get_href_size() > max_input_length) [[unlikely]] {
12565|  29.3k|            url.is_valid = false;
12566|  29.3k|          }
12567|  29.3k|        }
12568|  29.3k|      }
12569|  29.3k|    }
12570|  29.3k|  };
12571|       |
12572|       |  // We refuse to parse URL strings that exceed the maximum input length.
12573|       |  // By default, this is 4GB but can be configured via
12574|       |  // ada::set_max_input_length().
12575|  29.3k|  if (user_input.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12575:7): [True: 0, False: 29.3k]
  ------------------
12576|      0|    url.is_valid = false;
12577|      0|  }
12578|       |  // Going forward, user_input.size() is in [0,
12579|       |  // std::numeric_limits<uint32_t>::max). If we are provided with an invalid
12580|       |  // base, or the optional_url was invalid, we must return.
12581|  29.3k|  if (base_url != nullptr) {
  ------------------
  |  Branch (12581:7): [True: 3.93k, False: 25.4k]
  ------------------
12582|  3.93k|    url.is_valid &= base_url->is_valid;
12583|  3.93k|  }
12584|  29.3k|  if (!url.is_valid) {
  ------------------
  |  Branch (12584:7): [True: 0, False: 29.3k]
  ------------------
12585|      0|    return url;
12586|      0|  }
12587|       |
12588|       |  // Simple absolute / relative fast paths (before tabs/newline scan).
12589|       |  if constexpr (store_values) {
12590|       |    bool hit_fast_path = false;
12591|       |    if (base_url == nullptr) {
12592|       |      // IPv4/IPv6 and userinfo miss the fast path. Skip the never_inline
12593|       |      // call so those URLs (including SetHref) do not pay for a miss.
12594|       |      const auto* p = reinterpret_cast<const uint8_t*>(user_input.data());
12595|       |      const size_t n = user_input.size();
12596|       |      size_t host_start = 0;
12597|       |      if (n >= 8 && p[4] == ':' && p[5] == '/' && p[6] == '/') {
12598|       |        host_start = 7;
12599|       |      } else if (n >= 9 && p[5] == ':' && p[6] == '/' && p[7] == '/') {
12600|       |        host_start = 8;
12601|       |      }
12602|       |      const uint8_t host_first = host_start != 0 ? p[host_start] : 0;
12603|       |      const bool skip_ip =
12604|       |          host_first == '[' || (host_first >= '0' && host_first <= '9');
12605|       |      const bool skip_userinfo =
12606|       |          host_start != 0 && authority_has_at(p, host_start, n);
12607|       |      hit_fast_path = !skip_ip && !skip_userinfo &&
12608|       |                      try_parse_simple_absolute(user_input, url);
12609|       |    } else {
12610|       |      hit_fast_path = try_parse_simple_relative(user_input, *base_url, url);
12611|       |    }
12612|       |    if (hit_fast_path) {
12613|       |      if constexpr (result_type_is_ada_url_aggregator) {
12614|       |        if (url.buffer.size() > max_input_length) [[unlikely]] {
12615|       |          url.is_valid = false;
12616|       |        }
12617|       |      } else {
12618|       |        // For a small absolute input, even worst-case normalization cannot
12619|       |        // exceed the limit: percent encoding expands at most 3x and IDNA at
12620|       |        // most 4.5x. Avoid traversing every stored component on the common
12621|       |        // default-limit path.
12622|       |        if ((base_url != nullptr ||
12623|       |             user_input.size() > static_cast<size_t>(max_input_length) / 5) &&
12624|       |            url.get_href_size() > max_input_length) [[unlikely]] {
12625|       |          url.is_valid = false;
12626|       |        }
12627|       |      }
12628|       |      return url;
12629|       |    }
12630|       |  }
12631|       |
12632|  29.3k|  state state = state::SCHEME_START;
12633|       |
12634|  29.3k|  std::string tmp_buffer;
12635|  29.3k|  std::string_view url_data;
12636|  29.3k|  if (unicode::has_tabs_or_newline(user_input)) [[unlikely]] {
  ------------------
  |  Branch (12636:7): [True: 196, False: 29.1k]
  ------------------
12637|    196|    tmp_buffer = user_input;
12638|       |    // Optimization opportunity: Instead of copying and then pruning, we could
12639|       |    // just directly build the string from user_input.
12640|    196|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
12641|    196|    url_data = tmp_buffer;
12642|  29.1k|  } else [[likely]] {
12643|  29.1k|    url_data = user_input;
12644|  29.1k|  }
12645|       |
12646|       |  // Leading and trailing control characters are uncommon and easy to deal with
12647|       |  // (no performance concern).
12648|  29.3k|  helpers::trim_c0_whitespace(url_data);
12649|       |
12650|       |  if constexpr (result_type_is_ada_url_aggregator && store_values) {
12651|       |    // Most of the time, we just need user_input.size().
12652|       |    // In some instances, we may need a bit more.
12653|       |    ///////////////////////////
12654|       |    // This is *very* important. This line should *not* be removed
12655|       |    // hastily. There are principled reasons why reserve is important
12656|       |    // for performance. If you have a benchmark with small inputs,
12657|       |    // it may not matter, but in other instances, it could.
12658|       |    ////
12659|       |    // This rounds up to the next power of two.
12660|       |    // We know that user_input.size() is in [0,
12661|       |    // std::numeric_limits<uint32_t>::max).
12662|       |    uint32_t reserve_capacity =
12663|       |        (0xFFFFFFFF >>
12664|       |         helpers::leading_zeroes(uint32_t(1 | user_input.size()))) +
12665|       |        1;
12666|       |    url.reserve(reserve_capacity);
12667|       |  }
12668|       |
12669|       |  // Optimization opportunity. Most websites do not have fragment.
12670|  29.3k|  std::optional<std::string_view> fragment = helpers::prune_hash(url_data);
12671|       |  // We add it last so that an implementation like ada::url_aggregator
12672|       |  // can append it last to its internal buffer, thus improving performance.
12673|       |
12674|       |  // Here url_data no longer has its fragment.
12675|       |  // We are going to access the data from url_data (it is immutable).
12676|       |  // At any given time, we are pointing at byte 'input_position' in url_data.
12677|       |  // The input_position variable should range from 0 to input_size.
12678|       |  // It is illegal to access url_data at input_size.
12679|  29.3k|  size_t input_position = 0;
12680|  29.3k|  const size_t input_size = url_data.size();
12681|       |  // Keep running the following state machine by switching on state.
12682|       |  // If after a run pointer points to the EOF code point, go to the next step.
12683|       |  // Otherwise, increase pointer by 1 and continue with the state machine.
12684|       |  // We never decrement input_position.
12685|   126k|  while (input_position <= input_size) {
  ------------------
  |  Branch (12685:10): [True: 125k, False: 217]
  ------------------
12686|   125k|    ada_log("In parsing at ", input_position, " out of ", input_size,
12687|   125k|            " in state ", ada::to_string(state));
12688|   125k|    switch (state) {
12689|  29.3k|      case state::SCHEME_START: {
  ------------------
  |  Branch (12689:7): [True: 29.3k, False: 96.4k]
  ------------------
12690|  29.3k|        ada_log("SCHEME_START ", helpers::substring(url_data, input_position));
12691|       |        // If c is an ASCII alpha, append c, lowercased, to buffer, and set
12692|       |        // state to scheme state.
12693|  29.3k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (12693:13): [True: 21.5k, False: 7.88k]
  ------------------
12694|  21.5k|            checkers::is_alpha(url_data[input_position])) {
  ------------------
  |  Branch (12694:13): [True: 21.0k, False: 494]
  ------------------
12695|  21.0k|          state = state::SCHEME;
12696|  21.0k|          input_position++;
12697|  21.0k|        } else {
12698|       |          // Otherwise, if state override is not given, set state to no scheme
12699|       |          // state and decrease pointer by 1.
12700|  8.37k|          state = state::NO_SCHEME;
12701|  8.37k|        }
12702|  29.3k|        break;
12703|      0|      }
12704|  21.0k|      case state::SCHEME: {
  ------------------
  |  Branch (12704:7): [True: 21.0k, False: 104k]
  ------------------
12705|  21.0k|        ada_log("SCHEME ", helpers::substring(url_data, input_position));
12706|       |        // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.),
12707|       |        // append c, lowercased, to buffer.
12708|  50.2k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (12708:16): [True: 49.9k, False: 349]
  ------------------
12709|  49.9k|               (unicode::is_alnum_plus(url_data[input_position]))) {
  ------------------
  |  Branch (12709:16): [True: 29.2k, False: 20.6k]
  ------------------
12710|  29.2k|          input_position++;
12711|  29.2k|        }
12712|       |        // Otherwise, if c is U+003A (:), then:
12713|  21.0k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (12713:13): [True: 20.6k, False: 349]
  ------------------
12714|  20.6k|            (url_data[input_position] == ':')) {
  ------------------
  |  Branch (12714:13): [True: 20.5k, False: 131]
  ------------------
12715|  20.5k|          ada_log("SCHEME the scheme should be ",
12716|  20.5k|                  url_data.substr(0, input_position));
12717|       |          if constexpr (result_type_is_ada_url) {
12718|       |            if (!url.parse_scheme(url_data.substr(0, input_position))) {
12719|       |              return url;
12720|       |            }
12721|  20.5k|          } else {
12722|       |            // we pass the colon along instead of painfully adding it back.
12723|  20.5k|            if (!url.parse_scheme_with_colon(
  ------------------
  |  Branch (12723:17): [True: 0, False: 20.5k]
  ------------------
12724|  20.5k|                    url_data.substr(0, input_position + 1))) {
12725|      0|              return url;
12726|      0|            }
12727|  20.5k|          }
12728|  20.5k|          ada_log("SCHEME the scheme is ", url.get_protocol());
12729|       |
12730|       |          // If url's scheme is "file", then:
12731|       |          // NOLINTNEXTLINE(bugprone-branch-clone)
12732|  20.5k|          if (url.type == scheme::type::FILE) {
  ------------------
  |  Branch (12732:15): [True: 2.64k, False: 17.8k]
  ------------------
12733|       |            // Set state to file state.
12734|  2.64k|            state = state::FILE;
12735|  2.64k|          }
12736|       |          // Otherwise, if url is special, base is non-null, and base's scheme
12737|       |          // is url's scheme: Note: Doing base_url->scheme is unsafe if base_url
12738|       |          // != nullptr is false.
12739|  17.8k|          else if (url.is_special() && base_url != nullptr &&
  ------------------
  |  Branch (12739:20): [True: 12.3k, False: 5.54k]
  |  Branch (12739:40): [True: 2.12k, False: 10.2k]
  ------------------
12740|  2.12k|                   base_url->type == url.type) {
  ------------------
  |  Branch (12740:20): [True: 280, False: 1.84k]
  ------------------
12741|       |            // Set state to special relative or authority state.
12742|    280|            state = state::SPECIAL_RELATIVE_OR_AUTHORITY;
12743|    280|          }
12744|       |          // Otherwise, if url is special, set state to special authority
12745|       |          // slashes state.
12746|  17.6k|          else if (url.is_special()) {
  ------------------
  |  Branch (12746:20): [True: 12.0k, False: 5.54k]
  ------------------
12747|  12.0k|            state = state::SPECIAL_AUTHORITY_SLASHES;
12748|  12.0k|          }
12749|       |          // Otherwise, if remaining starts with an U+002F (/), set state to
12750|       |          // path or authority state and increase pointer by 1.
12751|  5.54k|          else if (input_position + 1 < input_size &&
  ------------------
  |  Branch (12751:20): [True: 3.19k, False: 2.35k]
  ------------------
12752|  3.19k|                   url_data[input_position + 1] == '/') {
  ------------------
  |  Branch (12752:20): [True: 2.21k, False: 980]
  ------------------
12753|  2.21k|            state = state::PATH_OR_AUTHORITY;
12754|  2.21k|            input_position++;
12755|  2.21k|          }
12756|       |          // Otherwise, set url's path to the empty string and set state to
12757|       |          // opaque path state.
12758|  3.33k|          else {
12759|  3.33k|            state = state::OPAQUE_PATH;
12760|  3.33k|          }
12761|  20.5k|        }
12762|       |        // Otherwise, if state override is not given, set buffer to the empty
12763|       |        // string, state to no scheme state, and start over (from the first code
12764|       |        // point in input).
12765|    480|        else {
12766|    480|          state = state::NO_SCHEME;
12767|    480|          input_position = 0;
12768|    480|          break;
12769|    480|        }
12770|  20.5k|        input_position++;
12771|  20.5k|        break;
12772|  21.0k|      }
12773|  8.85k|      case state::NO_SCHEME: {
  ------------------
  |  Branch (12773:7): [True: 8.85k, False: 116k]
  ------------------
12774|  8.85k|        ada_log("NO_SCHEME ", helpers::substring(url_data, input_position));
12775|       |        // The fragment was pruned from url_data before the state machine ran,
12776|       |        // so 'c is U+0023 (#)' holds exactly when a fragment was found and
12777|       |        // nothing else remains in front of it.
12778|  8.85k|        const bool c_is_hash =
12779|  8.85k|            fragment.has_value() && input_position == input_size;
  ------------------
  |  Branch (12779:13): [True: 147, False: 8.71k]
  |  Branch (12779:37): [True: 107, False: 40]
  ------------------
12780|       |        // If base is null, or base has an opaque path and c is not U+0023 (#),
12781|       |        // validation error, return failure.
12782|  8.85k|        if (base_url == nullptr || (base_url->has_opaque_path && !c_is_hash)) {
  ------------------
  |  Branch (12782:13): [True: 7.87k, False: 980]
  |  Branch (12782:37): [True: 118, False: 862]
  |  Branch (12782:66): [True: 47, False: 71]
  ------------------
12783|  7.92k|          ada_log("NO_SCHEME validation error");
12784|  7.92k|          url.is_valid = false;
12785|  7.92k|          return url;
12786|  7.92k|        }
12787|       |        // Otherwise, if base has an opaque path and c is U+0023 (#),
12788|       |        // set url's scheme to base's scheme, url's path to base's path, url's
12789|       |        // query to base's query, and set state to fragment state.
12790|    933|        else if (base_url->has_opaque_path && c_is_hash) {
  ------------------
  |  Branch (12790:18): [True: 71, False: 862]
  |  Branch (12790:47): [True: 71, False: 0]
  ------------------
12791|     71|          ada_log("NO_SCHEME opaque base with fragment");
12792|     71|          url.copy_scheme(*base_url);
12793|     71|          url.has_opaque_path = base_url->has_opaque_path;
12794|       |
12795|       |          if constexpr (result_type_is_ada_url) {
12796|       |            url.path = base_url->path;
12797|       |            url.query = base_url->query;
12798|     71|          } else {
12799|     71|            url.update_base_pathname(base_url->get_pathname());
12800|     71|            if (base_url->has_search()) {
  ------------------
  |  Branch (12800:17): [True: 0, False: 71]
  ------------------
12801|       |              // get_search() returns "" for an empty query string (URL ends
12802|       |              // with '?'). update_base_search("") would incorrectly clear the
12803|       |              // query, so pass "?" to preserve the empty query distinction.
12804|      0|              auto s = base_url->get_search();
12805|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (12805:38): [True: 0, False: 0]
  ------------------
12806|      0|            }
12807|     71|          }
12808|     71|          url.update_unencoded_base_hash(*fragment);
12809|     71|          enforce_max_input_length();
12810|     71|          return url;
12811|     71|        }
12812|       |        // Otherwise, if base's scheme is not "file", set state to relative
12813|       |        // state and decrease pointer by 1.
12814|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
12815|    862|        else if (base_url->type != scheme::type::FILE) {
  ------------------
  |  Branch (12815:18): [True: 639, False: 223]
  ------------------
12816|    639|          ada_log("NO_SCHEME non-file relative path");
12817|    639|          state = state::RELATIVE_SCHEME;
12818|    639|        }
12819|       |        // Otherwise, set state to file state and decrease pointer by 1.
12820|    223|        else {
12821|    223|          ada_log("NO_SCHEME file base type");
12822|    223|          state = state::FILE;
12823|    223|        }
12824|    862|        break;
12825|  8.85k|      }
12826|  13.1k|      case state::AUTHORITY: {
  ------------------
  |  Branch (12826:7): [True: 13.1k, False: 112k]
  ------------------
12827|  13.1k|        ada_log("AUTHORITY ", helpers::substring(url_data, input_position));
12828|       |        // most URLs have no @. Having no @ tells us that we don't have to worry
12829|       |        // about AUTHORITY. Of course, we could have @ and still not have to
12830|       |        // worry about AUTHORITY.
12831|       |        // TODO: Instead of just collecting a bool, collect the location of the
12832|       |        // '@' and do something useful with it.
12833|       |        // TODO: We could do various processing early on, using a single pass
12834|       |        // over the string to collect information about it, e.g., telling us
12835|       |        // whether there is a @ and if so, where (or how many).
12836|       |
12837|       |        // Check if url data contains an @.
12838|  13.1k|        if (url_data.find('@', input_position) == std::string_view::npos) {
  ------------------
  |  Branch (12838:13): [True: 11.9k, False: 1.23k]
  ------------------
12839|  11.9k|          state = state::HOST;
12840|  11.9k|          break;
12841|  11.9k|        }
12842|  1.23k|        bool at_sign_seen{false};
12843|  1.23k|        bool password_token_seen{false};
12844|       |        /**
12845|       |         * We expect something of the sort...
12846|       |         * https://user:pass@example.com:1234/foo/bar?baz#quux
12847|       |         * --------^
12848|       |         */
12849|  8.67k|        do {
12850|  8.67k|          std::string_view view = url_data.substr(input_position);
12851|       |          // The delimiters are @, /, ? \\.
12852|  8.67k|          size_t location =
12853|  8.67k|              url.is_special() ? helpers::find_authority_delimiter_special(view)
  ------------------
  |  Branch (12853:15): [True: 7.71k, False: 957]
  ------------------
12854|  8.67k|                               : helpers::find_authority_delimiter(view);
12855|  8.67k|          std::string_view authority_view = view.substr(0, location);
12856|  8.67k|          size_t end_of_authority = input_position + authority_view.size();
12857|       |          // If c is U+0040 (@), then:
12858|  8.67k|          if ((end_of_authority != input_size) &&
  ------------------
  |  Branch (12858:15): [True: 7.86k, False: 813]
  ------------------
12859|  7.86k|              (url_data[end_of_authority] == '@')) {
  ------------------
  |  Branch (12859:15): [True: 7.43k, False: 426]
  ------------------
12860|       |            // If atSignSeen is true, then prepend "%40" to buffer.
12861|  7.43k|            if (at_sign_seen) {
  ------------------
  |  Branch (12861:17): [True: 6.30k, False: 1.13k]
  ------------------
12862|  6.30k|              if (password_token_seen) {
  ------------------
  |  Branch (12862:19): [True: 2.49k, False: 3.81k]
  ------------------
12863|       |                if constexpr (result_type_is_ada_url) {
12864|       |                  url.password += "%40";
12865|  2.49k|                } else {
12866|  2.49k|                  url.append_base_password("%40");
12867|  2.49k|                }
12868|  3.81k|              } else {
12869|       |                if constexpr (result_type_is_ada_url) {
12870|       |                  url.username += "%40";
12871|  3.81k|                } else {
12872|  3.81k|                  url.append_base_username("%40");
12873|  3.81k|                }
12874|  3.81k|              }
12875|  6.30k|            }
12876|       |
12877|  7.43k|            at_sign_seen = true;
12878|       |
12879|  7.43k|            if (!password_token_seen) {
  ------------------
  |  Branch (12879:17): [True: 4.94k, False: 2.49k]
  ------------------
12880|  4.94k|              size_t password_token_location = authority_view.find(':');
12881|  4.94k|              password_token_seen =
12882|  4.94k|                  password_token_location != std::string_view::npos;
12883|       |
12884|       |              if constexpr (store_values) {
12885|       |                if (!password_token_seen) {
12886|       |                  if constexpr (result_type_is_ada_url) {
12887|       |                    url.username += unicode::percent_encode(
12888|       |                        authority_view,
12889|       |                        character_sets::USERINFO_PERCENT_ENCODE);
12890|       |                  } else {
12891|       |                    url.append_base_username(unicode::percent_encode(
12892|       |                        authority_view,
12893|       |                        character_sets::USERINFO_PERCENT_ENCODE));
12894|       |                  }
12895|       |                } else {
12896|       |                  if constexpr (result_type_is_ada_url) {
12897|       |                    url.username += unicode::percent_encode(
12898|       |                        authority_view.substr(0, password_token_location),
12899|       |                        character_sets::USERINFO_PERCENT_ENCODE);
12900|       |                    url.password += unicode::percent_encode(
12901|       |                        authority_view.substr(password_token_location + 1),
12902|       |                        character_sets::USERINFO_PERCENT_ENCODE);
12903|       |                  } else {
12904|       |                    url.append_base_username(unicode::percent_encode(
12905|       |                        authority_view.substr(0, password_token_location),
12906|       |                        character_sets::USERINFO_PERCENT_ENCODE));
12907|       |                    url.append_base_password(unicode::percent_encode(
12908|       |                        authority_view.substr(password_token_location + 1),
12909|       |                        character_sets::USERINFO_PERCENT_ENCODE));
12910|       |                  }
12911|       |                }
12912|       |              }
12913|  4.94k|            } else if constexpr (store_values) {
12914|  2.49k|              if constexpr (result_type_is_ada_url) {
12915|  2.49k|                url.password += unicode::percent_encode(
12916|  2.49k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE);
12917|  2.49k|              } else {
12918|  2.49k|                url.append_base_password(unicode::percent_encode(
12919|  2.49k|                    authority_view, character_sets::USERINFO_PERCENT_ENCODE));
12920|  2.49k|              }
12921|  2.49k|            }
12922|  7.43k|          }
12923|       |          // Otherwise, if one of the following is true:
12924|       |          // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
12925|       |          // - url is special and c is U+005C (\)
12926|  1.23k|          else if (end_of_authority == input_size ||
  ------------------
  |  Branch (12926:20): [True: 813, False: 426]
  ------------------
12927|    426|                   url_data[end_of_authority] == '/' ||
  ------------------
  |  Branch (12927:20): [True: 385, False: 41]
  ------------------
12928|     41|                   url_data[end_of_authority] == '?' ||
  ------------------
  |  Branch (12928:20): [True: 35, False: 6]
  ------------------
12929|  1.23k|                   (url.is_special() && url_data[end_of_authority] == '\\')) {
  ------------------
  |  Branch (12929:21): [True: 6, False: 0]
  |  Branch (12929:41): [True: 6, False: 0]
  ------------------
12930|       |            // If atSignSeen is true and authority_view is the empty string,
12931|       |            // validation error, return failure.
12932|  1.23k|            if (at_sign_seen && authority_view.empty()) {
  ------------------
  |  Branch (12932:17): [True: 1.13k, False: 104]
  |  Branch (12932:33): [True: 329, False: 806]
  ------------------
12933|    329|              url.is_valid = false;
12934|    329|              return url;
12935|    329|            }
12936|    910|            state = state::HOST;
12937|    910|            break;
12938|  1.23k|          }
12939|  7.43k|          if (end_of_authority == input_size) {
  ------------------
  |  Branch (12939:15): [True: 0, False: 7.43k]
  ------------------
12940|       |            if constexpr (store_values) {
12941|       |              if (fragment.has_value()) {
12942|       |                url.update_unencoded_base_hash(*fragment);
12943|       |              }
12944|       |            }
12945|      0|            enforce_max_input_length();
12946|      0|            return url;
12947|      0|          }
12948|  7.43k|          input_position = end_of_authority + 1;
12949|  7.43k|        } while (true);
  ------------------
  |  Branch (12949:18): [True: 7.43k, Folded]
  ------------------
12950|       |
12951|    910|        break;
12952|  1.23k|      }
12953|    910|      case state::SPECIAL_RELATIVE_OR_AUTHORITY: {
  ------------------
  |  Branch (12953:7): [True: 280, False: 125k]
  ------------------
12954|    280|        ada_log("SPECIAL_RELATIVE_OR_AUTHORITY ",
12955|    280|                helpers::substring(url_data, input_position));
12956|       |
12957|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
12958|       |        // then set state to special authority ignore slashes state and increase
12959|       |        // pointer by 1.
12960|    280|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (12960:13): [True: 203, False: 77]
  ------------------
12961|    203|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
12962|    203|          input_position += 2;
12963|    203|        } else {
12964|       |          // Otherwise, validation error, set state to relative state and
12965|       |          // decrease pointer by 1.
12966|     77|          state = state::RELATIVE_SCHEME;
12967|     77|        }
12968|       |
12969|    280|        break;
12970|  1.23k|      }
12971|  2.21k|      case state::PATH_OR_AUTHORITY: {
  ------------------
  |  Branch (12971:7): [True: 2.21k, False: 123k]
  ------------------
12972|  2.21k|        ada_log("PATH_OR_AUTHORITY ",
12973|  2.21k|                helpers::substring(url_data, input_position));
12974|       |
12975|       |        // If c is U+002F (/), then set state to authority state.
12976|  2.21k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (12976:13): [True: 2.10k, False: 111]
  ------------------
12977|  2.10k|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (12977:13): [True: 890, False: 1.21k]
  ------------------
12978|    890|          state = state::AUTHORITY;
12979|    890|          input_position++;
12980|  1.32k|        } else {
12981|       |          // Otherwise, set state to path state, and decrease pointer by 1.
12982|  1.32k|          state = state::PATH;
12983|  1.32k|        }
12984|       |
12985|  2.21k|        break;
12986|  1.23k|      }
12987|    716|      case state::RELATIVE_SCHEME: {
  ------------------
  |  Branch (12987:7): [True: 716, False: 125k]
  ------------------
12988|    716|        ada_log("RELATIVE_SCHEME ",
12989|    716|                helpers::substring(url_data, input_position));
12990|       |
12991|       |        // Set url's scheme to base's scheme.
12992|    716|        url.copy_scheme(*base_url);
12993|       |
12994|       |        // If c is U+002F (/), then set state to relative slash state.
12995|    716|        if ((input_position != input_size) &&
  ------------------
  |  Branch (12995:13): [True: 550, False: 166]
  ------------------
12996|       |            // NOLINTNEXTLINE(bugprone-branch-clone)
12997|    550|            (url_data[input_position] == '/')) {
  ------------------
  |  Branch (12997:13): [True: 73, False: 477]
  ------------------
12998|     73|          ada_log(
12999|     73|              "RELATIVE_SCHEME if c is U+002F (/), then set state to relative "
13000|     73|              "slash state");
13001|     73|          state = state::RELATIVE_SLASH;
13002|    643|        } else if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13002:20): [True: 513, False: 130]
  |  Branch (13002:40): [True: 385, False: 128]
  ------------------
13003|    385|                   (url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13003:20): [True: 5, False: 380]
  ------------------
13004|       |          // Otherwise, if url is special and c is U+005C (\), validation error,
13005|       |          // set state to relative slash state.
13006|      5|          ada_log(
13007|      5|              "RELATIVE_SCHEME  if url is special and c is U+005C, validation "
13008|      5|              "error, set state to relative slash state");
13009|      5|          state = state::RELATIVE_SLASH;
13010|    638|        } else {
13011|    638|          ada_log("RELATIVE_SCHEME otherwise");
13012|       |          // Set url's username to base's username, url's password to base's
13013|       |          // password, url's host to base's host, url's port to base's port,
13014|       |          // url's path to a clone of base's path, and url's query to base's
13015|       |          // query.
13016|       |          if constexpr (result_type_is_ada_url) {
13017|       |            url.username = base_url->username;
13018|       |            url.password = base_url->password;
13019|       |            url.host = base_url->host;
13020|       |            url.port = base_url->port;
13021|       |            // cloning the base path includes cloning the has_opaque_path flag
13022|       |            url.has_opaque_path = base_url->has_opaque_path;
13023|       |            url.path = base_url->path;
13024|       |            url.query = base_url->query;
13025|    638|          } else {
13026|    638|            url.update_base_authority(base_url->get_href(),
13027|    638|                                      base_url->get_components());
13028|    638|            url.update_host_to_base_host(base_url->get_hostname());
13029|    638|            url.update_base_port(base_url->retrieve_base_port());
13030|       |            // cloning the base path includes cloning the has_opaque_path flag
13031|    638|            url.has_opaque_path = base_url->has_opaque_path;
13032|    638|            url.update_base_pathname(base_url->get_pathname());
13033|    638|            if (base_url->has_search()) {
  ------------------
  |  Branch (13033:17): [True: 0, False: 638]
  ------------------
13034|       |              // get_search() returns "" for an empty query string (URL ends
13035|       |              // with '?'). update_base_search("") would incorrectly clear the
13036|       |              // query, so pass "?" to preserve the empty query distinction.
13037|      0|              auto s = base_url->get_search();
13038|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13038:38): [True: 0, False: 0]
  ------------------
13039|      0|            }
13040|    638|          }
13041|       |
13042|    638|          url.has_opaque_path = base_url->has_opaque_path;
13043|       |
13044|       |          // If c is U+003F (?), then set url's query to the empty string, and
13045|       |          // state to query state.
13046|    638|          if ((input_position != input_size) &&
  ------------------
  |  Branch (13046:15): [True: 472, False: 166]
  ------------------
13047|    472|              (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13047:15): [True: 30, False: 442]
  ------------------
13048|     30|            state = state::QUERY;
13049|     30|          }
13050|       |          // Otherwise, if c is not the EOF code point:
13051|    608|          else if (input_position != input_size) {
  ------------------
  |  Branch (13051:20): [True: 442, False: 166]
  ------------------
13052|       |            // Set url's query to null.
13053|    442|            url.clear_search();
13054|       |            if constexpr (result_type_is_ada_url) {
13055|       |              // Shorten url's path.
13056|       |              helpers::shorten_path(url.path, url.type);
13057|    442|            } else {
13058|    442|              std::string_view path = url.get_pathname();
13059|    442|              if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (13059:19): [True: 0, False: 442]
  ------------------
13060|      0|                url.update_base_pathname(std::move(std::string(path)));
13061|      0|              }
13062|    442|            }
13063|       |            // Set state to path state and decrease pointer by 1.
13064|    442|            state = state::PATH;
13065|    442|            break;
13066|    442|          }
13067|    638|        }
13068|    274|        input_position++;
13069|    274|        break;
13070|    716|      }
13071|     78|      case state::RELATIVE_SLASH: {
  ------------------
  |  Branch (13071:7): [True: 78, False: 125k]
  ------------------
13072|     78|        ada_log("RELATIVE_SLASH ",
13073|     78|                helpers::substring(url_data, input_position));
13074|       |
13075|       |        // If url is special and c is U+002F (/) or U+005C (\), then:
13076|       |        // NOLINTNEXTLINE(bugprone-branch-clone)
13077|     78|        if (url.is_special() && (input_position != input_size) &&
  ------------------
  |  Branch (13077:13): [True: 62, False: 16]
  |  Branch (13077:33): [True: 39, False: 23]
  ------------------
13078|     39|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13078:14): [True: 9, False: 30]
  ------------------
13079|     30|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13079:14): [True: 2, False: 28]
  ------------------
13080|       |          // Set state to special authority ignore slashes state.
13081|     11|          state = state::SPECIAL_AUTHORITY_IGNORE_SLASHES;
13082|     11|        }
13083|       |        // Otherwise, if c is U+002F (/), then set state to authority state.
13084|     67|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13084:18): [True: 38, False: 29]
  ------------------
13085|     38|                 (url_data[input_position] == '/')) {
  ------------------
  |  Branch (13085:18): [True: 6, False: 32]
  ------------------
13086|      6|          state = state::AUTHORITY;
13087|      6|        }
13088|       |        // Otherwise, set
13089|       |        // - url's username to base's username,
13090|       |        // - url's password to base's password,
13091|       |        // - url's host to base's host,
13092|       |        // - url's port to base's port,
13093|       |        // - state to path state, and then, decrease pointer by 1.
13094|     61|        else {
13095|       |          if constexpr (result_type_is_ada_url) {
13096|       |            url.username = base_url->username;
13097|       |            url.password = base_url->password;
13098|       |            url.host = base_url->host;
13099|       |            url.port = base_url->port;
13100|     61|          } else {
13101|     61|            url.update_base_authority(base_url->get_href(),
13102|     61|                                      base_url->get_components());
13103|     61|            url.update_host_to_base_host(base_url->get_hostname());
13104|     61|            url.update_base_port(base_url->retrieve_base_port());
13105|     61|          }
13106|     61|          state = state::PATH;
13107|     61|          break;
13108|     61|        }
13109|       |
13110|     17|        input_position++;
13111|     17|        break;
13112|     78|      }
13113|  12.0k|      case state::SPECIAL_AUTHORITY_SLASHES: {
  ------------------
  |  Branch (13113:7): [True: 12.0k, False: 113k]
  ------------------
13114|  12.0k|        ada_log("SPECIAL_AUTHORITY_SLASHES ",
13115|  12.0k|                helpers::substring(url_data, input_position));
13116|       |
13117|       |        // If c is U+002F (/) and remaining starts with U+002F (/),
13118|       |        // then set state to special authority ignore slashes state and increase
13119|       |        // pointer by 1.
13120|  12.0k|        if (url_data.substr(input_position, 2) == "//") {
  ------------------
  |  Branch (13120:13): [True: 2.95k, False: 9.10k]
  ------------------
13121|  2.95k|          input_position += 2;
13122|  2.95k|        }
13123|       |
13124|  12.0k|        [[fallthrough]];
13125|  12.0k|      }
13126|  12.2k|      case state::SPECIAL_AUTHORITY_IGNORE_SLASHES: {
  ------------------
  |  Branch (13126:7): [True: 214, False: 125k]
  ------------------
13127|  12.2k|        ada_log("SPECIAL_AUTHORITY_IGNORE_SLASHES ",
13128|  12.2k|                helpers::substring(url_data, input_position));
13129|       |
13130|       |        // If c is neither U+002F (/) nor U+005C (\), then set state to
13131|       |        // authority state and decrease pointer by 1.
13132|  12.8k|        while ((input_position != input_size) &&
  ------------------
  |  Branch (13132:16): [True: 12.8k, False: 57]
  ------------------
13133|  12.8k|               ((url_data[input_position] == '/') ||
  ------------------
  |  Branch (13133:17): [True: 369, False: 12.4k]
  ------------------
13134|  12.4k|                (url_data[input_position] == '\\'))) {
  ------------------
  |  Branch (13134:17): [True: 223, False: 12.2k]
  ------------------
13135|    592|          input_position++;
13136|    592|        }
13137|  12.2k|        state = state::AUTHORITY;
13138|       |
13139|  12.2k|        break;
13140|  12.0k|      }
13141|     34|      case state::QUERY: {
  ------------------
  |  Branch (13141:7): [True: 34, False: 125k]
  ------------------
13142|     34|        ada_log("QUERY ", helpers::substring(url_data, input_position));
13143|       |        if constexpr (store_values) {
13144|       |          // Let queryPercentEncodeSet be the special-query percent-encode set
13145|       |          // if url is special; otherwise the query percent-encode set.
13146|       |          const uint8_t* query_percent_encode_set =
13147|       |              url.is_special() ? character_sets::SPECIAL_QUERY_PERCENT_ENCODE
13148|       |                               : character_sets::QUERY_PERCENT_ENCODE;
13149|       |
13150|       |          // Percent-encode after encoding, with encoding, buffer, and
13151|       |          // queryPercentEncodeSet, and append the result to url's query.
13152|       |          url.update_base_search(url_data.substr(input_position),
13153|       |                                 query_percent_encode_set);
13154|       |          ada_log("QUERY update_base_search completed ");
13155|       |          if (fragment.has_value()) {
13156|       |            url.update_unencoded_base_hash(*fragment);
13157|       |          }
13158|       |        }
13159|     34|        enforce_max_input_length();
13160|     34|        return url;
13161|  12.0k|      }
13162|  12.8k|      case state::HOST: {
  ------------------
  |  Branch (13162:7): [True: 12.8k, False: 113k]
  ------------------
13163|  12.8k|        ada_log("HOST ", helpers::substring(url_data, input_position));
13164|       |
13165|  12.8k|        std::string_view host_view = url_data.substr(input_position);
13166|  12.8k|        auto [location, found_colon] =
13167|  12.8k|            helpers::get_host_delimiter_location(url.is_special(), host_view);
13168|  12.8k|        input_position = (location != std::string_view::npos)
  ------------------
  |  Branch (13168:26): [True: 12.8k, False: 0]
  ------------------
13169|  12.8k|                             ? input_position + location
13170|  12.8k|                             : input_size;
13171|       |        // Otherwise, if c is U+003A (:) and insideBrackets is false, then:
13172|       |        // Note: the 'found_colon' value is true if and only if a colon was
13173|       |        // encountered while not inside brackets.
13174|  12.8k|        if (found_colon) {
  ------------------
  |  Branch (13174:13): [True: 2.19k, False: 10.6k]
  ------------------
13175|       |          // If buffer is the empty string, validation error, return failure.
13176|       |          // Let host be the result of host parsing buffer with url is not
13177|       |          // special.
13178|  2.19k|          ada_log("HOST parsing ", host_view);
13179|  2.19k|          if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13179:15): [True: 218, False: 1.97k]
  ------------------
13180|    218|            return url;
13181|    218|          }
13182|  1.97k|          ada_log("HOST parsing results in ", url.get_hostname());
13183|       |          // Set url's host to host, buffer to the empty string, and state to
13184|       |          // port state.
13185|  1.97k|          state = state::PORT;
13186|  1.97k|          input_position++;
13187|  1.97k|        }
13188|       |        // Otherwise, if one of the following is true:
13189|       |        // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
13190|       |        // - url is special and c is U+005C (\)
13191|       |        // The get_host_delimiter_location function either brings us to
13192|       |        // the colon outside of the bracket, or to one of those characters.
13193|  10.6k|        else {
13194|       |          // If url is special and host_view is the empty string, validation
13195|       |          // error, return failure.
13196|  10.6k|          if (host_view.empty() && url.is_special()) {
  ------------------
  |  Branch (13196:15): [True: 190, False: 10.4k]
  |  Branch (13196:36): [True: 60, False: 130]
  ------------------
13197|     60|            url.is_valid = false;
13198|     60|            return url;
13199|     60|          }
13200|  10.5k|          ada_log("HOST parsing ", host_view, " href=", url.get_href());
13201|       |          // Let host be the result of host parsing host_view with url is not
13202|       |          // special.
13203|  10.5k|          if (host_view.empty()) {
  ------------------
  |  Branch (13203:15): [True: 130, False: 10.4k]
  ------------------
13204|    130|            url.update_base_hostname("");
13205|  10.4k|          } else if (!url.parse_host(host_view)) {
  ------------------
  |  Branch (13205:22): [True: 2.50k, False: 7.94k]
  ------------------
13206|  2.50k|            return url;
13207|  2.50k|          }
13208|  8.07k|          ada_log("HOST parsing results in ", url.get_hostname(),
13209|  8.07k|                  " href=", url.get_href());
13210|       |
13211|       |          // Set url's host to host, and state to path start state.
13212|  8.07k|          state = state::PATH_START;
13213|  8.07k|        }
13214|       |
13215|  10.0k|        break;
13216|  12.8k|      }
13217|  10.0k|      case state::OPAQUE_PATH: {
  ------------------
  |  Branch (13217:7): [True: 3.33k, False: 122k]
  ------------------
13218|  3.33k|        ada_log("OPAQUE_PATH ", helpers::substring(url_data, input_position));
13219|       |        // Opaque path, query, and fragment are structurally always valid:
13220|       |        // the parser would just percent-encode whatever is there. When we
13221|       |        // are not storing values (can_parse), we can return immediately.
13222|       |        // We must set has_opaque_path = true before returning so that when
13223|       |        // this URL is used as an internal base inside can_parse, NO_SCHEME
13224|       |        // correctly rejects relative inputs against an opaque-path base
13225|       |        // (e.g. can_parse("", &"W:") must return false).
13226|  3.33k|        if constexpr (!store_values) {
13227|  3.33k|          url.has_opaque_path = true;
13228|  3.33k|          return url;
13229|  3.33k|        }
13230|      0|        std::string_view view = url_data.substr(input_position);
13231|       |        // If c is U+003F (?), then set url's query to the empty string and
13232|       |        // state to query state.
13233|  3.33k|        size_t location = view.find('?');
13234|  3.33k|        if (location != std::string_view::npos) {
  ------------------
  |  Branch (13234:13): [True: 0, False: 3.33k]
  ------------------
13235|      0|          view.remove_suffix(view.size() - location);
13236|      0|          state = state::QUERY;
13237|      0|          input_position += location + 1;
13238|  3.33k|        } else {
13239|  3.33k|          input_position = input_size + 1;
13240|  3.33k|        }
13241|  3.33k|        url.has_opaque_path = true;
13242|       |
13243|       |        // This is a really unlikely scenario in real world. We should not seek
13244|       |        // to optimize it.
13245|  3.33k|        if (view.ends_with(' ')) {
  ------------------
  |  Branch (13245:13): [True: 0, False: 3.33k]
  ------------------
13246|      0|          std::string modified_view =
13247|      0|              std::string(view.substr(0, view.size() - 1)) + "%20";
13248|      0|          url.update_base_pathname(unicode::percent_encode(
13249|      0|              modified_view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13250|  3.33k|        } else {
13251|  3.33k|          url.update_base_pathname(unicode::percent_encode(
13252|  3.33k|              view, character_sets::C0_CONTROL_PERCENT_ENCODE));
13253|  3.33k|        }
13254|  3.33k|        break;
13255|  12.8k|      }
13256|  1.97k|      case state::PORT: {
  ------------------
  |  Branch (13256:7): [True: 1.97k, False: 123k]
  ------------------
13257|  1.97k|        ada_log("PORT ", helpers::substring(url_data, input_position));
13258|  1.97k|        std::string_view port_view = url_data.substr(input_position);
13259|  1.97k|        input_position += url.parse_port(port_view, true);
13260|  1.97k|        if (!url.is_valid) {
  ------------------
  |  Branch (13260:13): [True: 81, False: 1.89k]
  ------------------
13261|     81|          return url;
13262|     81|        }
13263|  1.89k|        state = state::PATH_START;
13264|  1.89k|        [[fallthrough]];
13265|  1.89k|      }
13266|  11.8k|      case state::PATH_START: {
  ------------------
  |  Branch (13266:7): [True: 9.96k, False: 115k]
  ------------------
13267|  11.8k|        ada_log("PATH_START ", helpers::substring(url_data, input_position));
13268|       |        // Path, query, and fragment are structurally always valid: the
13269|       |        // parser would just percent-encode whatever is there. When we are
13270|       |        // not storing values (can_parse), we can return immediately since
13271|       |        // no subsequent state can invalidate the URL.
13272|  11.8k|        if constexpr (!store_values) {
13273|  11.8k|          return url;
13274|  11.8k|        }
13275|       |
13276|       |        // If url is special, then:
13277|  11.8k|        if (url.is_special()) {
  ------------------
  |  Branch (13277:13): [True: 0, False: 11.8k]
  ------------------
13278|       |          // Set state to path state.
13279|      0|          state = state::PATH;
13280|       |
13281|       |          // Optimization: Avoiding going into PATH state improves the
13282|       |          // performance of urls ending with /.
13283|      0|          if (input_position == input_size) {
  ------------------
  |  Branch (13283:15): [True: 0, False: 0]
  ------------------
13284|       |            if constexpr (store_values) {
13285|       |              url.update_base_pathname("/");
13286|       |              if (fragment.has_value()) {
13287|       |                url.update_unencoded_base_hash(*fragment);
13288|       |              }
13289|       |            }
13290|      0|            enforce_max_input_length();
13291|      0|            return url;
13292|      0|          }
13293|       |          // If c is neither U+002F (/) nor U+005C (\), then decrease pointer
13294|       |          // by 1. We know that (input_position == input_size) is impossible
13295|       |          // here, because of the previous if-check.
13296|      0|          if ((url_data[input_position] != '/') &&
  ------------------
  |  Branch (13296:15): [True: 0, False: 0]
  ------------------
13297|      0|              (url_data[input_position] != '\\')) {
  ------------------
  |  Branch (13297:15): [True: 0, False: 0]
  ------------------
13298|      0|            break;
13299|      0|          }
13300|      0|        }
13301|       |        // Otherwise, if state override is not given and c is U+003F (?),
13302|       |        // set url's query to the empty string and state to query state.
13303|  11.8k|        else if ((input_position != input_size) &&
  ------------------
  |  Branch (13303:18): [True: 0, False: 11.8k]
  ------------------
13304|      0|                 (url_data[input_position] == '?')) {
  ------------------
  |  Branch (13304:18): [True: 0, False: 0]
  ------------------
13305|      0|          state = state::QUERY;
13306|      0|        }
13307|       |        // Otherwise, if c is not the EOF code point:
13308|  11.8k|        else if (input_position != input_size) {
  ------------------
  |  Branch (13308:18): [True: 0, False: 11.8k]
  ------------------
13309|       |          // Set state to path state.
13310|      0|          state = state::PATH;
13311|       |
13312|       |          // If c is not U+002F (/), then decrease pointer by 1.
13313|      0|          if (url_data[input_position] != '/') {
  ------------------
  |  Branch (13313:15): [True: 0, False: 0]
  ------------------
13314|      0|            break;
13315|      0|          }
13316|      0|        }
13317|       |
13318|  11.8k|        input_position++;
13319|  11.8k|        break;
13320|  11.8k|      }
13321|  2.54k|      case state::PATH: {
  ------------------
  |  Branch (13321:7): [True: 2.54k, False: 123k]
  ------------------
13322|  2.54k|        ada_log("PATH ", helpers::substring(url_data, input_position));
13323|       |        // Path, query, and fragment are structurally always valid: the
13324|       |        // parser would just percent-encode whatever is there. When we are
13325|       |        // not storing values (can_parse), we can return immediately since
13326|       |        // no subsequent state can invalidate the URL.
13327|  2.54k|        if constexpr (!store_values) {
13328|  2.54k|          return url;
13329|  2.54k|        }
13330|      0|        std::string_view view = url_data.substr(input_position);
13331|       |
13332|       |        // Most time, we do not need percent encoding.
13333|       |        // Furthermore, we can immediately locate the '?'.
13334|  2.54k|        size_t locofquestionmark = view.find('?');
13335|  2.54k|        if (locofquestionmark != std::string_view::npos) {
  ------------------
  |  Branch (13335:13): [True: 0, False: 2.54k]
  ------------------
13336|      0|          state = state::QUERY;
13337|      0|          view.remove_suffix(view.size() - locofquestionmark);
13338|      0|          input_position += locofquestionmark + 1;
13339|  2.54k|        } else {
13340|  2.54k|          input_position = input_size + 1;
13341|  2.54k|        }
13342|       |        if constexpr (store_values) {
13343|       |          if constexpr (result_type_is_ada_url) {
13344|       |            helpers::parse_prepared_path(view, url.type, url.path);
13345|       |          } else {
13346|       |            url.consume_prepared_path(view);
13347|       |            ADA_ASSERT_TRUE(url.validate());
13348|       |          }
13349|       |        }
13350|  2.54k|        break;
13351|  11.8k|      }
13352|  2.21k|      case state::FILE_SLASH: {
  ------------------
  |  Branch (13352:7): [True: 2.21k, False: 123k]
  ------------------
13353|  2.21k|        ada_log("FILE_SLASH ", helpers::substring(url_data, input_position));
13354|       |
13355|       |        // If c is U+002F (/) or U+005C (\), then:
13356|  2.21k|        if ((input_position != input_size) &&
  ------------------
  |  Branch (13356:13): [True: 2.15k, False: 58]
  ------------------
13357|  2.15k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13357:14): [True: 2.10k, False: 52]
  ------------------
13358|  2.10k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13358:14): [True: 6, False: 46]
  ------------------
13359|  2.10k|          ada_log("FILE_SLASH c is U+002F or U+005C");
13360|       |          // Set state to file host state.
13361|  2.10k|          state = state::FILE_HOST;
13362|  2.10k|          input_position++;
13363|  2.10k|        } else {
13364|    104|          ada_log("FILE_SLASH otherwise");
13365|       |          // If base is non-null and base's scheme is "file", then:
13366|       |          // Note: it is unsafe to do base_url->scheme unless you know that
13367|       |          // base_url_has_value() is true.
13368|    104|          if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13368:15): [True: 81, False: 23]
  |  Branch (13368:38): [True: 78, False: 3]
  ------------------
13369|       |            // Set url's host to base's host.
13370|       |            if constexpr (result_type_is_ada_url) {
13371|       |              url.host = base_url->host;
13372|     78|            } else {
13373|     78|              url.update_host_to_base_host(base_url->get_host());
13374|     78|            }
13375|       |            // If the code point substring from pointer to the end of input does
13376|       |            // not start with a Windows drive letter and base's path[0] is a
13377|       |            // normalized Windows drive letter, then append base's path[0] to
13378|       |            // url's path.
13379|     78|            if (!base_url->get_pathname().empty()) {
  ------------------
  |  Branch (13379:17): [True: 0, False: 78]
  ------------------
13380|      0|              if (!checkers::is_windows_drive_letter(
  ------------------
  |  Branch (13380:19): [True: 0, False: 0]
  ------------------
13381|      0|                      url_data.substr(input_position))) {
13382|      0|                std::string_view first_base_url_path =
13383|      0|                    base_url->get_pathname().substr(1);
13384|      0|                size_t loc = first_base_url_path.find('/');
13385|      0|                if (loc != std::string_view::npos) {
  ------------------
  |  Branch (13385:21): [True: 0, False: 0]
  ------------------
13386|      0|                  helpers::resize(first_base_url_path, loc);
13387|      0|                }
13388|      0|                if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (13388:21): [True: 0, False: 0]
  ------------------
13389|      0|                        first_base_url_path)) {
13390|       |                  if constexpr (result_type_is_ada_url) {
13391|       |                    url.path += '/';
13392|       |                    url.path += first_base_url_path;
13393|      0|                  } else {
13394|      0|                    url.append_base_pathname(
13395|      0|                        helpers::concat("/", first_base_url_path));
13396|      0|                  }
13397|      0|                }
13398|      0|              }
13399|      0|            }
13400|     78|          }
13401|       |
13402|       |          // Set state to path state, and decrease pointer by 1.
13403|    104|          state = state::PATH;
13404|    104|        }
13405|       |
13406|  2.21k|        break;
13407|  11.8k|      }
13408|  2.10k|      case state::FILE_HOST: {
  ------------------
  |  Branch (13408:7): [True: 2.10k, False: 123k]
  ------------------
13409|  2.10k|        ada_log("FILE_HOST ", helpers::substring(url_data, input_position));
13410|  2.10k|        std::string_view view = url_data.substr(input_position);
13411|       |
13412|  2.10k|        size_t location = view.find_first_of("/\\?");
13413|  2.10k|        std::string_view file_host_buffer = view.substr(
13414|  2.10k|            0, (location != std::string_view::npos) ? location : view.size());
  ------------------
  |  Branch (13414:16): [True: 898, False: 1.21k]
  ------------------
13415|       |
13416|  2.10k|        if (checkers::is_windows_drive_letter(file_host_buffer)) {
  ------------------
  |  Branch (13416:13): [True: 12, False: 2.09k]
  ------------------
13417|     12|          state = state::PATH;
13418|  2.09k|        } else if (file_host_buffer.empty()) {
  ------------------
  |  Branch (13418:20): [True: 284, False: 1.81k]
  ------------------
13419|       |          // Set url's host to the empty string.
13420|       |          if constexpr (result_type_is_ada_url) {
13421|       |            url.host = "";
13422|    284|          } else {
13423|    284|            url.update_base_hostname("");
13424|    284|          }
13425|       |          // Set state to path start state.
13426|    284|          state = state::PATH_START;
13427|  1.81k|        } else {
13428|  1.81k|          size_t consumed_bytes = file_host_buffer.size();
13429|  1.81k|          input_position += consumed_bytes;
13430|       |          // Let host be the result of host parsing buffer with url is not
13431|       |          // special.
13432|  1.81k|          if (!url.parse_host(file_host_buffer)) {
  ------------------
  |  Branch (13432:15): [True: 213, False: 1.60k]
  ------------------
13433|    213|            return url;
13434|    213|          }
13435|       |
13436|       |          if constexpr (result_type_is_ada_url) {
13437|       |            // If host is "localhost", then set host to the empty string.
13438|       |            if (url.host.has_value() && url.host.value() == "localhost") {
13439|       |              url.host = "";
13440|       |            }
13441|  1.60k|          } else {
13442|  1.60k|            if (url.get_hostname() == "localhost") {
  ------------------
  |  Branch (13442:17): [True: 6, False: 1.59k]
  ------------------
13443|      6|              url.update_base_hostname("");
13444|      6|            }
13445|  1.60k|          }
13446|       |
13447|       |          // Set buffer to the empty string and state to path start state.
13448|  1.60k|          state = state::PATH_START;
13449|  1.60k|        }
13450|       |
13451|  1.89k|        break;
13452|  2.10k|      }
13453|  2.87k|      case state::FILE: {
  ------------------
  |  Branch (13453:7): [True: 2.87k, False: 122k]
  ------------------
13454|  2.87k|        ada_log("FILE ", helpers::substring(url_data, input_position));
13455|  2.87k|        std::string_view file_view = url_data.substr(input_position);
13456|       |
13457|  2.87k|        url.set_protocol_as_file();
13458|       |        if constexpr (result_type_is_ada_url) {
13459|       |          // Set url's host to the empty string.
13460|       |          url.host = "";
13461|  2.87k|        } else {
13462|  2.87k|          url.update_base_hostname("");
13463|  2.87k|        }
13464|       |        // If c is U+002F (/) or U+005C (\), then:
13465|  2.87k|        if (input_position != input_size &&
  ------------------
  |  Branch (13465:13): [True: 2.74k, False: 125]
  ------------------
13466|  2.74k|            (url_data[input_position] == '/' ||
  ------------------
  |  Branch (13466:14): [True: 2.20k, False: 539]
  ------------------
13467|  2.21k|             url_data[input_position] == '\\')) {
  ------------------
  |  Branch (13467:14): [True: 7, False: 532]
  ------------------
13468|  2.21k|          ada_log("FILE c is U+002F or U+005C");
13469|       |          // Set state to file slash state.
13470|  2.21k|          state = state::FILE_SLASH;
13471|  2.21k|        }
13472|       |        // Otherwise, if base is non-null and base's scheme is "file":
13473|    657|        else if (base_url != nullptr && base_url->type == scheme::type::FILE) {
  ------------------
  |  Branch (13473:18): [True: 242, False: 415]
  |  Branch (13473:41): [True: 166, False: 76]
  ------------------
13474|       |          // Set url's host to base's host, url's path to a clone of base's
13475|       |          // path, and url's query to base's query.
13476|    166|          ada_log("FILE base non-null");
13477|       |          if constexpr (result_type_is_ada_url) {
13478|       |            url.host = base_url->host;
13479|       |            url.path = base_url->path;
13480|       |            url.query = base_url->query;
13481|    166|          } else {
13482|    166|            url.update_host_to_base_host(base_url->get_hostname());
13483|    166|            url.update_base_pathname(base_url->get_pathname());
13484|    166|            if (base_url->has_search()) {
  ------------------
  |  Branch (13484:17): [True: 0, False: 166]
  ------------------
13485|       |              // get_search() returns "" for an empty query string (URL ends
13486|       |              // with '?'). update_base_search("") would incorrectly clear the
13487|       |              // query, so pass "?" to preserve the empty query distinction.
13488|      0|              auto s = base_url->get_search();
13489|      0|              url.update_base_search(s.empty() ? std::string_view("?") : s);
  ------------------
  |  Branch (13489:38): [True: 0, False: 0]
  ------------------
13490|      0|            }
13491|    166|          }
13492|    166|          url.has_opaque_path = base_url->has_opaque_path;
13493|       |
13494|       |          // If c is U+003F (?), then set url's query to the empty string and
13495|       |          // state to query state.
13496|    166|          if (input_position != input_size && url_data[input_position] == '?') {
  ------------------
  |  Branch (13496:15): [True: 115, False: 51]
  |  Branch (13496:47): [True: 4, False: 111]
  ------------------
13497|      4|            state = state::QUERY;
13498|      4|          }
13499|       |          // Otherwise, if c is not the EOF code point:
13500|    162|          else if (input_position != input_size) {
  ------------------
  |  Branch (13500:20): [True: 111, False: 51]
  ------------------
13501|       |            // Set url's query to null.
13502|    111|            url.clear_search();
13503|       |            // If the code point substring from pointer to the end of input does
13504|       |            // not start with a Windows drive letter, then shorten url's path.
13505|    111|            if (!checkers::is_windows_drive_letter(file_view)) {
  ------------------
  |  Branch (13505:17): [True: 92, False: 19]
  ------------------
13506|       |              if constexpr (result_type_is_ada_url) {
13507|       |                helpers::shorten_path(url.path, url.type);
13508|     92|              } else {
13509|     92|                std::string_view path = url.get_pathname();
13510|     92|                if (helpers::shorten_path(path, url.type)) {
  ------------------
  |  Branch (13510:21): [True: 0, False: 92]
  ------------------
13511|      0|                  url.update_base_pathname(std::move(std::string(path)));
13512|      0|                }
13513|     92|              }
13514|     92|            }
13515|       |            // Otherwise:
13516|     19|            else {
13517|       |              // Set url's path to an empty list.
13518|     19|              url.clear_pathname();
13519|     19|              url.has_opaque_path = true;
13520|     19|            }
13521|       |
13522|       |            // Set state to path state and decrease pointer by 1.
13523|    111|            state = state::PATH;
13524|    111|            break;
13525|    111|          }
13526|    166|        }
13527|       |        // Otherwise, set state to path state, and decrease pointer by 1.
13528|    491|        else {
13529|    491|          ada_log("FILE go to path");
13530|    491|          state = state::PATH;
13531|    491|          break;
13532|    491|        }
13533|       |
13534|  2.26k|        input_position++;
13535|  2.26k|        break;
13536|  2.87k|      }
13537|      0|      default:
  ------------------
  |  Branch (13537:7): [True: 0, False: 125k]
  ------------------
13538|      0|        unreachable();
13539|   125k|    }
13540|   125k|  }
13541|       |  if constexpr (store_values) {
13542|       |    if (fragment.has_value()) {
13543|       |      url.update_unencoded_base_hash(*fragment);
13544|       |    }
13545|       |  }
13546|    217|  enforce_max_input_length();
13547|    217|  return url;
13548|  29.3k|}
_ZNK3ada14url_aggregator8get_hostEv:
14497|    156|    ada_lifetime_bound {
14498|    156|  ada_log("url_aggregator::get_host");
14499|       |  // Technically, we should check if there is a hostname, but
14500|       |  // the code below works even if there isn't.
14501|       |  // if(!has_hostname()) { return ""; }
14502|    156|  size_t start = components.host_start;
14503|    156|  if (components.host_end > components.host_start &&
  ------------------
  |  Branch (14503:7): [True: 38, False: 118]
  ------------------
14504|     38|      buffer[components.host_start] == '@') {
  ------------------
  |  Branch (14504:7): [True: 0, False: 38]
  ------------------
14505|      0|    start++;
14506|      0|  }
14507|       |  // if we have an empty host, then the space between components.host_end and
14508|       |  // components.pathname_start may be occupied by /.
14509|    156|  if (start == components.host_end) {
  ------------------
  |  Branch (14509:7): [True: 118, False: 38]
  ------------------
14510|    118|    return {};
14511|    118|  }
14512|     38|  return helpers::substring(buffer, start, components.pathname_start);
14513|    156|}
_ZNK3ada14url_aggregator12get_hostnameEv:
14516|  18.5k|    ada_lifetime_bound {
14517|  18.5k|  ada_log("url_aggregator::get_hostname");
14518|       |  // Technically, we should check if there is a hostname, but
14519|       |  // the code below works even if there isn't.
14520|       |  // if(!has_hostname()) { return ""; }
14521|  18.5k|  size_t start = components.host_start;
14522|       |  // So host_start is not where the host begins.
14523|  18.5k|  if (components.host_end > components.host_start &&
  ------------------
  |  Branch (14523:7): [True: 4.42k, False: 14.1k]
  ------------------
14524|  4.42k|      buffer[components.host_start] == '@') {
  ------------------
  |  Branch (14524:7): [True: 238, False: 4.19k]
  ------------------
14525|    238|    start++;
14526|    238|  }
14527|  18.5k|  return helpers::substring(buffer, start, components.host_end);
14528|  18.5k|}
_ZNK3ada14url_aggregator10get_searchEv:
14531|    152|    ada_lifetime_bound {
14532|    152|  ada_log("url_aggregator::get_search");
14533|       |  // If this's URL's query is either null or the empty string, then return the
14534|       |  // empty string. Return U+003F (?), followed by this's URL's query.
14535|    152|  if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (14535:7): [True: 0, False: 152]
  ------------------
14536|      0|    return "";
14537|      0|  }
14538|    152|  auto ending_index = uint32_t(buffer.size());
14539|    152|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (14539:7): [True: 49, False: 103]
  ------------------
14540|     49|    ending_index = components.hash_start;
14541|     49|  }
14542|    152|  if (ending_index - components.search_start <= 1) {
  ------------------
  |  Branch (14542:7): [True: 62, False: 90]
  ------------------
14543|     62|    return "";
14544|     62|  }
14545|     90|  return helpers::substring(buffer, components.search_start, ending_index);
14546|    152|}
_ZNK3ada14url_aggregator12get_protocolEv:
14549|  1.43k|    ada_lifetime_bound {
14550|  1.43k|  ada_log("url_aggregator::get_protocol");
14551|  1.43k|  return helpers::substring(buffer, 0, components.protocol_end);
14552|  1.43k|}
_ZN3ada14url_aggregator10parse_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
14661|  3.58k|bool url_aggregator::parse_ipv4(std::string_view input, bool in_place) {
14662|  3.58k|  ada_log("parse_ipv4 ", input, " [", input.size(),
14663|  3.58k|          " bytes], overlaps with buffer: ",
14664|  3.58k|          helpers::overlaps(input, buffer) ? "yes" : "no");
14665|  3.58k|  ADA_ASSERT_TRUE(validate());
14666|  3.58k|  if (input.empty()) {
  ------------------
  |  Branch (14666:7): [True: 0, False: 3.58k]
  ------------------
14667|      0|    return is_valid = false;
14668|      0|  }
14669|  3.58k|  const bool trailing_dot = (input.back() == '.');
14670|  3.58k|  if (trailing_dot) {
  ------------------
  |  Branch (14670:7): [True: 144, False: 3.44k]
  ------------------
14671|    144|    input.remove_suffix(1);
14672|    144|    if (input.empty()) {
  ------------------
  |  Branch (14672:9): [True: 0, False: 144]
  ------------------
14673|      0|      return is_valid = false;
14674|      0|    }
14675|    144|  }
14676|       |
14677|  3.58k|  const uint64_t fast = checkers::try_parse_ipv4_fast(input);
14678|  3.58k|  if (fast < checkers::ipv4_fast_fail) [[likely]] {
  ------------------
  |  Branch (14678:7): [True: 52, False: 3.53k]
  ------------------
14679|       |    // Pure decimal: keep the buffer when it already holds the address.
14680|       |    // Otherwise write the (trailing-dot-stripped) input.
14681|     52|    if (!(in_place && !trailing_dot)) {
  ------------------
  |  Branch (14681:11): [True: 26, False: 26]
  |  Branch (14681:23): [True: 6, False: 20]
  ------------------
14682|     46|      if (helpers::overlaps(input, buffer)) {
  ------------------
  |  Branch (14682:11): [True: 0, False: 46]
  ------------------
14683|      0|        update_base_hostname(std::string(input));
14684|     46|      } else {
14685|     46|        update_base_hostname(input);
14686|     46|      }
14687|     46|    }
14688|     52|    host_type = IPV4;
14689|     52|    ADA_ASSERT_TRUE(validate());
14690|     52|    return true;
14691|     52|  }
14692|       |
14693|  3.53k|  const char* p = input.data();
14694|  3.53k|  const char* end = p + input.size();
14695|  3.53k|  uint64_t ipv4 = 0;
14696|  3.53k|  int digit_count = 0;
14697|  3.53k|  int pure_decimal_count = 0;
14698|       |
14699|  4.51k|  for (; digit_count < 4 && p < end; ++digit_count) {
  ------------------
  |  Branch (14699:10): [True: 4.49k, False: 24]
  |  Branch (14699:29): [True: 4.49k, False: 0]
  ------------------
14700|  4.49k|    uint64_t segment = 0;
14701|  4.49k|    bool pure = false;
14702|  4.49k|    if (!detail::parse_ipv4_number(p, end, segment, pure)) {
  ------------------
  |  Branch (14702:9): [True: 686, False: 3.80k]
  ------------------
14703|    686|      return is_valid = false;
14704|    686|    }
14705|  3.80k|    if (pure) {
  ------------------
  |  Branch (14705:9): [True: 2.73k, False: 1.07k]
  ------------------
14706|  2.73k|      ++pure_decimal_count;
14707|  2.73k|    }
14708|  3.80k|    if (p >= end) {
  ------------------
  |  Branch (14708:9): [True: 2.75k, False: 1.05k]
  ------------------
14709|  2.75k|      const unsigned shift = static_cast<unsigned>(32 - digit_count * 8);
14710|  2.75k|      if (segment >= (uint64_t{1} << shift)) {
  ------------------
  |  Branch (14710:11): [True: 50, False: 2.70k]
  ------------------
14711|     50|        return is_valid = false;
14712|     50|      }
14713|  2.70k|      ipv4 = (ipv4 << shift) | segment;
14714|  2.70k|      goto ipv4_done;
14715|  2.75k|    }
14716|  1.05k|    if (segment > 255 || *p != '.') {
  ------------------
  |  Branch (14716:9): [True: 66, False: 986]
  |  Branch (14716:26): [True: 0, False: 986]
  ------------------
14717|     66|      return is_valid = false;
14718|     66|    }
14719|    986|    ipv4 = (ipv4 << 8) | segment;
14720|    986|    ++p;
14721|    986|  }
14722|     24|  if (digit_count != 4 || p != end) {
  ------------------
  |  Branch (14722:7): [True: 0, False: 24]
  |  Branch (14722:27): [True: 24, False: 0]
  ------------------
14723|     24|    return is_valid = false;
14724|     24|  }
14725|  2.70k|ipv4_done:
14726|  2.70k|  ada_log("url_aggregator::parse_ipv4 completed ", get_href(),
14727|  2.70k|          " host: ", get_host());
14728|  2.70k|  if (in_place && pure_decimal_count == 4 && !trailing_dot) {
  ------------------
  |  Branch (14728:7): [True: 2.49k, False: 216]
  |  Branch (14728:19): [True: 0, False: 2.49k]
  |  Branch (14728:46): [True: 0, False: 0]
  ------------------
14729|      0|    ada_log(
14730|      0|        "url_aggregator::parse_ipv4 completed and was already correct in the "
14731|      0|        "buffer");
14732|  2.70k|  } else {
14733|  2.70k|    update_base_hostname(ada::serializers::ipv4(ipv4));
14734|  2.70k|  }
14735|  2.70k|  host_type = IPV4;
14736|  2.70k|  ADA_ASSERT_TRUE(validate());
14737|  2.70k|  return true;
14738|     24|}
_ZN3ada14url_aggregator10parse_ipv6ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14740|  1.06k|bool url_aggregator::parse_ipv6(std::string_view input) {
14741|  1.06k|  ada_log("parse_ipv6 ", input, " [", input.size(), " bytes]");
14742|  1.06k|  ADA_ASSERT_TRUE(validate());
14743|  1.06k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14744|  1.06k|  if (input.empty() || input.size() > 45) [[unlikely]] {
  ------------------
  |  Branch (14744:7): [True: 32, False: 1.03k]
  |  Branch (14744:24): [True: 26, False: 1.01k]
  ------------------
14745|     58|    return is_valid = false;
14746|     58|  }
14747|  1.01k|  std::array<uint16_t, 8> address{};
14748|       |#if defined(ADA_AVX512_IPV6)
14749|       |  if (bool simd_valid; detail::try_parse_ipv6_avx512(input.data(), input.size(),
14750|       |                                                     address, simd_valid)) {
14751|       |    if (!simd_valid) [[unlikely]] {
14752|       |      return is_valid = false;
14753|       |    }
14754|       |    const std::string serialized = ada::serializers::ipv6(address);
14755|       |    if (get_hostname() != serialized) {
14756|       |      update_base_hostname(serialized);
14757|       |    }
14758|       |    host_type = IPV6;
14759|       |    return true;
14760|       |  }
14761|       |  address = {};
14762|       |#endif
14763|  1.01k|  const char* pointer = input.data();
14764|  1.01k|  const char* const end = pointer + input.size();
14765|  1.01k|  int piece_index = 0;
14766|  1.01k|  int compress = -1;
14767|       |
14768|  1.01k|  if (*pointer == ':') {
  ------------------
  |  Branch (14768:7): [True: 394, False: 616]
  ------------------
14769|    394|    if (input.size() == 1 || pointer[1] != ':') [[unlikely]] {
  ------------------
  |  Branch (14769:9): [True: 6, False: 388]
  |  Branch (14769:30): [True: 10, False: 378]
  ------------------
14770|     16|      return is_valid = false;
14771|     16|    }
14772|    378|    pointer += 2;
14773|    378|    compress = ++piece_index;
14774|    378|  }
14775|       |
14776|  2.71k|  while (pointer != end) {
  ------------------
  |  Branch (14776:10): [True: 2.03k, False: 674]
  ------------------
14777|  2.03k|    if (piece_index == 8) [[unlikely]] {
  ------------------
  |  Branch (14777:9): [True: 6, False: 2.03k]
  ------------------
14778|      6|      return is_valid = false;
14779|      6|    }
14780|  2.03k|    if (*pointer == ':') {
  ------------------
  |  Branch (14780:9): [True: 274, False: 1.75k]
  ------------------
14781|    274|      if (compress != -1) [[unlikely]] {
  ------------------
  |  Branch (14781:11): [True: 6, False: 268]
  ------------------
14782|      6|        return is_valid = false;
14783|      6|      }
14784|    268|      ++pointer;
14785|    268|      compress = ++piece_index;
14786|    268|      continue;
14787|    274|    }
14788|       |
14789|  1.75k|    uint16_t value = 0;
14790|  1.75k|    const int length = detail::parse_hex_piece(pointer, end, value);
14791|       |
14792|  1.75k|    if (pointer != end && *pointer == '.') {
  ------------------
  |  Branch (14792:9): [True: 1.38k, False: 370]
  |  Branch (14792:27): [True: 190, False: 1.19k]
  ------------------
14793|    190|      if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (14793:11): [True: 6, False: 184]
  ------------------
14794|      6|        return is_valid = false;
14795|      6|      }
14796|    184|      pointer -= length;
14797|    184|      if (piece_index > 6) [[unlikely]] {
  ------------------
  |  Branch (14797:11): [True: 6, False: 178]
  ------------------
14798|      6|        return is_valid = false;
14799|      6|      }
14800|       |
14801|    178|      int numbers_seen = 0;
14802|    518|      while (pointer != end) {
  ------------------
  |  Branch (14802:14): [True: 458, False: 60]
  ------------------
14803|    458|        int ipv4_piece = -1;
14804|    458|        if (numbers_seen > 0) {
  ------------------
  |  Branch (14804:13): [True: 280, False: 178]
  ------------------
14805|    280|          if (*pointer == '.' && numbers_seen < 4) {
  ------------------
  |  Branch (14805:15): [True: 246, False: 34]
  |  Branch (14805:34): [True: 236, False: 10]
  ------------------
14806|    236|            ++pointer;
14807|    236|          } else {
14808|     44|            return is_valid = false;
14809|     44|          }
14810|    280|        }
14811|    414|        if (pointer == end || *pointer < '0' || *pointer > '9') [[unlikely]] {
  ------------------
  |  Branch (14811:13): [True: 38, False: 376]
  |  Branch (14811:31): [True: 6, False: 370]
  |  Branch (14811:49): [True: 12, False: 358]
  ------------------
14812|     56|          return is_valid = false;
14813|     56|        }
14814|    358|        ipv4_piece = *pointer - '0';
14815|    358|        ++pointer;
14816|    358|        if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (14816:13): [True: 322, False: 36]
  |  Branch (14816:31): [True: 128, False: 194]
  |  Branch (14816:50): [True: 118, False: 10]
  ------------------
14817|    118|          if (ipv4_piece == 0) [[unlikely]] {
  ------------------
  |  Branch (14817:15): [True: 6, False: 112]
  ------------------
14818|      6|            return is_valid = false;
14819|      6|          }
14820|    112|          ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
14821|    112|          ++pointer;
14822|    112|          if (pointer != end && *pointer >= '0' && *pointer <= '9') {
  ------------------
  |  Branch (14822:15): [True: 100, False: 12]
  |  Branch (14822:33): [True: 62, False: 38]
  |  Branch (14822:52): [True: 54, False: 8]
  ------------------
14823|     54|            ipv4_piece = ipv4_piece * 10 + (*pointer - '0');
14824|     54|            ++pointer;
14825|     54|            if (ipv4_piece > 255) [[unlikely]] {
  ------------------
  |  Branch (14825:17): [True: 12, False: 42]
  ------------------
14826|     12|              return is_valid = false;
14827|     12|            }
14828|     54|          }
14829|    112|        }
14830|    340|        address[static_cast<size_t>(piece_index)] = static_cast<uint16_t>(
14831|    340|            address[static_cast<size_t>(piece_index)] * 0x100 +
14832|    340|            static_cast<uint16_t>(ipv4_piece));
14833|    340|        ++numbers_seen;
14834|    340|        if (numbers_seen == 2 || numbers_seen == 4) {
  ------------------
  |  Branch (14834:13): [True: 106, False: 234]
  |  Branch (14834:34): [True: 34, False: 200]
  ------------------
14835|    140|          ++piece_index;
14836|    140|        }
14837|    340|      }
14838|     60|      if (numbers_seen != 4) [[unlikely]] {
  ------------------
  |  Branch (14838:11): [True: 36, False: 24]
  ------------------
14839|     36|        return is_valid = false;
14840|     36|      }
14841|     24|      break;
14842|     60|    }
14843|       |
14844|  1.56k|    if (length == 0) [[unlikely]] {
  ------------------
  |  Branch (14844:9): [True: 106, False: 1.46k]
  ------------------
14845|    106|      return is_valid = false;
14846|    106|    }
14847|       |
14848|  1.46k|    if (pointer != end && *pointer == ':') {
  ------------------
  |  Branch (14848:9): [True: 1.09k, False: 370]
  |  Branch (14848:27): [True: 1.08k, False: 6]
  ------------------
14849|  1.08k|      ++pointer;
14850|  1.08k|      if (pointer == end) [[unlikely]] {
  ------------------
  |  Branch (14850:11): [True: 6, False: 1.08k]
  ------------------
14851|      6|        return is_valid = false;
14852|      6|      }
14853|  1.08k|    } else if (pointer != end) [[unlikely]] {
  ------------------
  |  Branch (14853:16): [True: 6, False: 370]
  ------------------
14854|      6|      return is_valid = false;
14855|      6|    }
14856|       |
14857|  1.45k|    address[static_cast<size_t>(piece_index)] = value;
14858|  1.45k|    ++piece_index;
14859|  1.45k|  }
14860|       |
14861|    698|  if (compress != -1) {
  ------------------
  |  Branch (14861:7): [True: 628, False: 70]
  ------------------
14862|    628|    const int right = piece_index - compress;
14863|    628|    if (right > 0) {
  ------------------
  |  Branch (14863:9): [True: 324, False: 304]
  ------------------
14864|    324|      const size_t dest = static_cast<size_t>(8 - right);
14865|    324|      const size_t src = static_cast<size_t>(compress);
14866|    324|      if (dest != src) {
  ------------------
  |  Branch (14866:11): [True: 310, False: 14]
  ------------------
14867|    858|        for (size_t i = static_cast<size_t>(right); i-- > 0;) {
  ------------------
  |  Branch (14867:53): [True: 548, False: 310]
  ------------------
14868|    548|          address[dest + i] = address[src + i];
14869|    548|          address[src + i] = 0;
14870|    548|        }
14871|    310|      }
14872|    324|    }
14873|    628|  } else if (piece_index != 8) [[unlikely]] {
  ------------------
  |  Branch (14873:14): [True: 46, False: 24]
  ------------------
14874|     46|    return is_valid = false;
14875|     46|  }
14876|       |
14877|       |  // Serialize once; skip rewrite when hostname is already canonical.
14878|    652|  const std::string serialized = ada::serializers::ipv6(address);
14879|    652|  const std::string_view current = get_hostname();
14880|    652|  if (current.size() == serialized.size() &&
  ------------------
  |  Branch (14880:7): [True: 0, False: 652]
  ------------------
14881|      0|      std::memcmp(current.data(), serialized.data(), serialized.size()) == 0) {
  ------------------
  |  Branch (14881:7): [True: 0, False: 0]
  ------------------
14882|      0|    ada_log("parse_ipv6 in-place canonical match");
14883|    652|  } else {
14884|    652|    update_base_hostname(serialized);
14885|    652|  }
14886|    652|  ada_log("parse_ipv6 ", get_hostname());
14887|    652|  ADA_ASSERT_TRUE(validate());
14888|    652|  host_type = IPV6;
14889|    652|  return true;
14890|    698|}
_ZN3ada14url_aggregator17parse_opaque_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14892|  1.25k|bool url_aggregator::parse_opaque_host(std::string_view input) {
14893|  1.25k|  ada_log("parse_opaque_host ", input, " [", input.size(), " bytes]");
14894|  1.25k|  ADA_ASSERT_TRUE(validate());
14895|  1.25k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14896|  1.25k|  if (std::ranges::any_of(input, ada::unicode::is_forbidden_host_code_point)) {
  ------------------
  |  Branch (14896:7): [True: 20, False: 1.23k]
  ------------------
14897|     20|    return is_valid = false;
14898|     20|  }
14899|       |
14900|       |  // Return the result of running UTF-8 percent-encode on input using the C0
14901|       |  // control percent-encode set.
14902|  1.23k|  size_t idx = ada::unicode::percent_encode_index(
14903|  1.23k|      input, character_sets::C0_CONTROL_PERCENT_ENCODE);
14904|  1.23k|  if (idx == input.size()) {
  ------------------
  |  Branch (14904:7): [True: 706, False: 524]
  ------------------
14905|    706|    update_base_hostname(input);
14906|    706|  } else {
14907|       |    // We only create a temporary string if we need to.
14908|    524|    update_base_hostname(ada::unicode::percent_encode(
14909|    524|        input, character_sets::C0_CONTROL_PERCENT_ENCODE, idx));
14910|    524|  }
14911|  1.23k|  ADA_ASSERT_TRUE(validate());
14912|  1.23k|  return true;
14913|  1.25k|}
_ZN3ada14url_aggregator15delete_dash_dotEv:
15072|      9|void url_aggregator::delete_dash_dot() {
15073|      9|  ada_log("url_aggregator::delete_dash_dot");
15074|      9|  ADA_ASSERT_TRUE(validate());
15075|      9|  ADA_ASSERT_TRUE(has_dash_dot());
15076|      9|  buffer.erase(components.host_end, 2);
15077|      9|  components.pathname_start -= 2;
15078|      9|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (15078:7): [True: 0, False: 9]
  ------------------
15079|      0|    components.search_start -= 2;
15080|      0|  }
15081|      9|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (15081:7): [True: 0, False: 9]
  ------------------
15082|      0|    components.hash_start -= 2;
15083|      0|  }
15084|      9|  ADA_ASSERT_TRUE(validate());
15085|      9|  ADA_ASSERT_TRUE(!has_dash_dot());
15086|      9|}
can_parse.cc:_ZZN3ada4idna22utf32_length_from_utf8EPKcmENK3$_0clEa:
  272|   458k|  return std::count_if(p, std::next(p, len), [](int8_t c) {
  273|       |    // -65 is 0b10111111, anything larger in two-complement's
  274|       |    // should start a new code point.
  275|   458k|    return c > -65;
  276|   458k|  });
_ZN3ada4idna7deflate22make_fixed_litlen_huffEv:
  483|      2|inline Huff make_fixed_litlen_huff() {
  484|      2|  Huff h;
  485|      2|  uint8_t lens[288];
  486|       |  // Fixed Huffman: 0-143:8, 144-255:9, 256-279:7, 280-287:8
  487|    290|  for (int i = 0; i <= 143; i++) lens[i] = 8;
  ------------------
  |  Branch (487:19): [True: 288, False: 2]
  ------------------
  488|    226|  for (int i = 144; i <= 255; i++) lens[i] = 9;
  ------------------
  |  Branch (488:21): [True: 224, False: 2]
  ------------------
  489|     50|  for (int i = 256; i <= 279; i++) lens[i] = 7;
  ------------------
  |  Branch (489:21): [True: 48, False: 2]
  ------------------
  490|     18|  for (int i = 280; i <= 287; i++) lens[i] = 8;
  ------------------
  |  Branch (490:21): [True: 16, False: 2]
  ------------------
  491|      2|  h.build(lens, 288);
  492|      2|  return h;
  493|      2|}
_ZN3ada4idna7deflate4Huff5buildEPKhi:
  432|     16|  bool build(const uint8_t* lens, int n) {
  433|     16|    std::memset(counts, 0, sizeof(counts));
  434|     16|    std::memset(first_code, 0, sizeof(first_code));
  435|     16|    std::memset(base_idx, 0, sizeof(base_idx));
  436|     16|    nsymbols = n;
  437|     16|    max_bits = 0;
  438|  1.98k|    for (int i = 0; i < n; i++) {
  ------------------
  |  Branch (438:21): [True: 1.96k, False: 16]
  ------------------
  439|  1.96k|      lengths[i] = lens[i];
  440|  1.96k|      if (lens[i]) {
  ------------------
  |  Branch (440:11): [True: 1.91k, False: 49]
  ------------------
  441|  1.91k|        counts[lens[i]]++;
  442|  1.91k|        if (lens[i] > max_bits) max_bits = lens[i];
  ------------------
  |  Branch (442:13): [True: 46, False: 1.87k]
  ------------------
  443|  1.91k|      }
  444|  1.96k|    }
  445|       |    // Canonical first code for each bit length (RFC 1951).
  446|     16|    int code = 0;
  447|    256|    for (int bits = 1; bits <= MAXBITS; bits++) {
  ------------------
  |  Branch (447:24): [True: 240, False: 16]
  ------------------
  448|    240|      code = (code + counts[bits - 1]) << 1;
  449|    240|      first_code[bits] = code;
  450|    240|    }
  451|     16|    int idx = 0;
  452|    158|    for (int bits = 1; bits <= max_bits; bits++) {
  ------------------
  |  Branch (452:24): [True: 142, False: 16]
  ------------------
  453|    142|      base_idx[bits] = idx;
  454|       |      // assign symbols in symbol order for codes of this length
  455|  21.6k|      for (int sym = 0; sym < n; sym++) {
  ------------------
  |  Branch (455:25): [True: 21.5k, False: 142]
  ------------------
  456|  21.5k|        if (lengths[sym] == bits) {
  ------------------
  |  Branch (456:13): [True: 1.91k, False: 19.6k]
  ------------------
  457|  1.91k|          symbols[idx++] = static_cast<int16_t>(sym);
  458|  1.91k|        }
  459|  21.5k|      }
  460|    142|    }
  461|     16|    return true;
  462|     16|  }
_ZN3ada4idna7deflate20make_fixed_dist_huffEv:
  495|      2|inline Huff make_fixed_dist_huff() {
  496|      2|  Huff h;
  497|      2|  uint8_t lens[32];
  498|     66|  for (int i = 0; i < 32; i++) lens[i] = 5;
  ------------------
  |  Branch (498:19): [True: 64, False: 2]
  ------------------
  499|      2|  h.build(lens, 32);
  500|      2|  return h;
  501|      2|}
_ZN3ada4idna7deflate9BitReader6ensureEi:
  392|   457k|  bool ensure(int n) {
  393|   520k|    while (bits < n) {
  ------------------
  |  Branch (393:12): [True: 62.3k, False: 457k]
  ------------------
  394|  62.3k|      if (p >= end) return false;
  ------------------
  |  Branch (394:11): [True: 0, False: 62.3k]
  ------------------
  395|  62.3k|      acc |= uint32_t(*p++) << bits;
  396|  62.3k|      bits += 8;
  397|  62.3k|    }
  398|   457k|    return true;
  399|   457k|  }
can_parse.cc:_ZZN3ada4idna9ascii_mapEPcmENK3$_0clEh:
 5127|  18.2k|  auto broadcast = [](uint8_t v) -> uint64_t {
 5128|  18.2k|    return 0x101010101010101ull * v;
 5129|  18.2k|  };
_ZN3ada4idna13ensure_tablesEv:
 4878|  73.6k|[[nodiscard]] inline bool ensure_tables() noexcept {
 4879|  73.6k|  uint8_t state = tables_init_state.load(std::memory_order_acquire);
 4880|  73.6k|  if (state == kTablesReady) {
  ------------------
  |  Branch (4880:7): [True: 73.6k, False: 1]
  ------------------
 4881|  73.6k|    return true;
 4882|  73.6k|  }
 4883|      1|  if (state == kTablesFailed) {
  ------------------
  |  Branch (4883:7): [True: 0, False: 1]
  ------------------
 4884|      0|    return false;
 4885|      0|  }
 4886|       |
 4887|      1|  uint8_t expected = kTablesUninit;
 4888|      1|  if (tables_init_state.compare_exchange_strong(expected, kTablesInProgress,
  ------------------
  |  Branch (4888:7): [True: 1, False: 0]
  ------------------
 4889|      1|                                                std::memory_order_acq_rel,
 4890|      1|                                                std::memory_order_acquire)) {
 4891|      1|    uint8_t* buffer = new (std::nothrow) uint8_t[table_blob::uncompressed_size];
 4892|      1|    if (buffer == nullptr) {
  ------------------
  |  Branch (4892:9): [True: 0, False: 1]
  ------------------
 4893|      0|      tables_init_state.store(kTablesFailed, std::memory_order_release);
 4894|      0|      return false;
 4895|      0|    }
 4896|       |
 4897|      1|    const size_t n = deflate::inflate_raw(table_blob::compressed,
 4898|      1|                                          table_blob::compressed_size, buffer,
 4899|      1|                                          table_blob::uncompressed_size);
 4900|      1|    if (n != table_blob::uncompressed_size ||
  ------------------
  |  Branch (4900:9): [True: 0, False: 1]
  ------------------
 4901|      1|        crc32_ieee(buffer, n) != table_blob::uncompressed_crc32) {
  ------------------
  |  Branch (4901:9): [True: 0, False: 1]
  ------------------
 4902|      0|      delete[] buffer;
 4903|      0|      tables_init_state.store(kTablesFailed, std::memory_order_release);
 4904|      0|      return false;
 4905|      0|    }
 4906|       |
 4907|       |    // LE payload verified; convert multi-byte fields for big-endian hosts.
 4908|      1|    detail::convert_table_blob_to_host_endian(buffer);
 4909|       |
 4910|      1|    auto at = [&](size_t off) noexcept -> const uint8_t* {
 4911|      1|      return buffer + off;
 4912|      1|    };
 4913|       |
 4914|       |    // Publish all non-atomic pointers before READY. Waiters synchronize via
 4915|       |    // acquire on tables_init_state and then observe these writes.
 4916|      1|    idna_stage1 =
 4917|      1|        reinterpret_cast<const uint16_t*>(at(table_blob::off_idna_stage1));
 4918|      1|    idna_stage2 =
 4919|      1|        reinterpret_cast<const uint16_t*>(at(table_blob::off_idna_stage2));
 4920|      1|    idna_bool_blocks =
 4921|      1|        reinterpret_cast<const uint64_t*>(at(table_blob::off_idna_bool_blocks));
 4922|      1|    idna_utf8_mappings = at(table_blob::off_idna_utf8_mappings);
 4923|       |
 4924|      1|    decomposition_index = at(table_blob::off_decomposition_index);
 4925|      1|    decomposition_block_flat = reinterpret_cast<const uint16_t*>(
 4926|      1|        at(table_blob::off_decomposition_block));
 4927|      1|    decomposition_data = reinterpret_cast<const char32_t*>(
 4928|      1|        at(table_blob::off_decomposition_data));
 4929|      1|    ccc_index = at(table_blob::off_ccc_index);
 4930|      1|    ccc_block_flat = at(table_blob::off_ccc_block);
 4931|      1|    composition_index = at(table_blob::off_composition_index);
 4932|      1|    composition_block_flat = reinterpret_cast<const uint16_t*>(
 4933|      1|        at(table_blob::off_composition_block));
 4934|      1|    composition_data =
 4935|      1|        reinterpret_cast<const char32_t*>(at(table_blob::off_composition_data));
 4936|       |
 4937|      1|    id_continue =
 4938|      1|        reinterpret_cast<range_pair_ptr>(at(table_blob::off_id_continue_flat));
 4939|      1|    id_start =
 4940|      1|        reinterpret_cast<range_pair_ptr>(at(table_blob::off_id_start_flat));
 4941|       |
 4942|      1|    dir_start =
 4943|      1|        reinterpret_cast<const uint32_t*>(at(table_blob::off_dir_start));
 4944|      1|    dir_final =
 4945|      1|        reinterpret_cast<const uint32_t*>(at(table_blob::off_dir_final));
 4946|      1|    dir_value = at(table_blob::off_dir_value);
 4947|      1|    combining_ranges =
 4948|      1|        reinterpret_cast<range_pair_ptr>(at(table_blob::off_combining_flat));
 4949|       |
 4950|      1|    tables_buffer = buffer;
 4951|      1|    tables_init_state.store(kTablesReady, std::memory_order_release);
 4952|      1|    return true;
 4953|      1|  }
 4954|       |
 4955|       |  // Another thread owns init (or finished between our loads).
 4956|      0|  for (uint64_t spins = 0; spins < kTablesSpinLimit; ++spins) {
  ------------------
  |  Branch (4956:28): [True: 0, False: 0]
  ------------------
 4957|      0|    state = tables_init_state.load(std::memory_order_acquire);
 4958|      0|    if (state == kTablesReady) {
  ------------------
  |  Branch (4958:9): [True: 0, False: 0]
  ------------------
 4959|      0|      return true;
 4960|      0|    }
 4961|      0|    if (state == kTablesFailed) {
  ------------------
  |  Branch (4961:9): [True: 0, False: 0]
  ------------------
 4962|      0|      return false;
 4963|      0|    }
 4964|      0|#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \
 4965|      0|    defined(_M_IX86)
 4966|      0|#if defined(__GNUC__) || defined(__clang__)
 4967|      0|    __builtin_ia32_pause();
 4968|      0|#endif
 4969|       |#elif defined(__aarch64__) || defined(_M_ARM64)
 4970|       |#if defined(__GNUC__) || defined(__clang__)
 4971|       |    asm volatile("yield" ::: "memory");
 4972|       |#endif
 4973|       |#endif
 4974|      0|  }
 4975|       |  // Timed out waiting for a peer - treat as failure rather than hang.
 4976|      0|  return false;
 4977|      0|}
_ZN3ada4idna7deflate11inflate_rawEPKhmPhm:
  528|      1|                          size_t dst_cap) {
  529|      1|  BitReader br(src, src_len);
  530|      1|  size_t out = 0;
  531|      4|  for (;;) {
  532|      4|    uint32_t bfinal = br.get(1);
  533|      4|    uint32_t btype = br.get(2);
  534|      4|    if (bfinal == UINT32_MAX || btype == UINT32_MAX) return 0;
  ------------------
  |  Branch (534:9): [True: 0, False: 4]
  |  Branch (534:33): [True: 0, False: 4]
  ------------------
  535|      4|    if (btype == 0) {
  ------------------
  |  Branch (535:9): [True: 0, False: 4]
  ------------------
  536|       |      // stored
  537|      0|      br.align_byte();
  538|      0|      if (!br.ensure(16)) return 0;
  ------------------
  |  Branch (538:11): [True: 0, False: 0]
  ------------------
  539|       |      // read 16+16 from bit buffer awkwardly - use byte path
  540|       |      // Align: bits is multiple of 8
  541|       |      // Get len from remaining bits then bytes
  542|      0|      uint32_t len = br.get(16);
  543|      0|      uint32_t nlen = br.get(16);
  544|      0|      if (len == UINT32_MAX || nlen == UINT32_MAX) return 0;
  ------------------
  |  Branch (544:11): [True: 0, False: 0]
  |  Branch (544:32): [True: 0, False: 0]
  ------------------
  545|      0|      if ((len ^ 0xFFFF) != nlen) return 0;
  ------------------
  |  Branch (545:11): [True: 0, False: 0]
  ------------------
  546|       |      // After get, may have bits in acc from previous - for stored, should be
  547|       |      // byte aligned Copy len bytes from p
  548|      0|      if (br.bits != 0) {
  ------------------
  |  Branch (548:11): [True: 0, False: 0]
  ------------------
  549|       |        // leftover bits should be 0 if aligned
  550|      0|      }
  551|      0|      if (size_t(br.end - br.p) < len) return 0;
  ------------------
  |  Branch (551:11): [True: 0, False: 0]
  ------------------
  552|      0|      if (out + len > dst_cap) return 0;
  ------------------
  |  Branch (552:11): [True: 0, False: 0]
  ------------------
  553|      0|      std::memcpy(dst + out, br.p, len);
  554|      0|      br.p += len;
  555|      0|      out += len;
  556|      4|    } else if (btype == 1 || btype == 2) {
  ------------------
  |  Branch (556:16): [True: 0, False: 4]
  |  Branch (556:30): [True: 4, False: 0]
  ------------------
  557|      4|      Huff lit, dist;
  558|      4|      if (btype == 1) {
  ------------------
  |  Branch (558:11): [True: 0, False: 4]
  ------------------
  559|       |        // use fixed via functions
  560|      4|      } else {
  561|       |        // dynamic
  562|      4|        uint32_t hlit = br.get(5);
  563|      4|        if (hlit == UINT32_MAX) return 0;
  ------------------
  |  Branch (563:13): [True: 0, False: 4]
  ------------------
  564|      4|        hlit += 257;
  565|      4|        uint32_t hdist = br.get(5);
  566|      4|        if (hdist == UINT32_MAX) return 0;
  ------------------
  |  Branch (566:13): [True: 0, False: 4]
  ------------------
  567|      4|        hdist += 1;
  568|      4|        uint32_t hclen = br.get(4);
  569|      4|        if (hclen == UINT32_MAX) return 0;
  ------------------
  |  Branch (569:13): [True: 0, False: 4]
  ------------------
  570|      4|        hclen += 4;
  571|      4|        static const int order[19] = {16, 17, 18, 0, 8,  7, 9,  6, 10, 5,
  572|      4|                                      11, 4,  12, 3, 13, 2, 14, 1, 15};
  573|      4|        uint8_t clen[19]{};
  574|     69|        for (uint32_t i = 0; i < hclen; i++) {
  ------------------
  |  Branch (574:30): [True: 65, False: 4]
  ------------------
  575|     65|          uint32_t v = br.get(3);
  576|     65|          if (v == UINT32_MAX) return 0;
  ------------------
  |  Branch (576:15): [True: 0, False: 65]
  ------------------
  577|     65|          clen[order[i]] = uint8_t(v);
  578|     65|        }
  579|      4|        Huff cl;
  580|      4|        if (!cl.build(clen, 19)) return 0;
  ------------------
  |  Branch (580:13): [True: 0, False: 4]
  ------------------
  581|      4|        uint8_t lens[288 + 32]{};
  582|      4|        uint32_t n = hlit + hdist;
  583|  1.09k|        for (uint32_t i = 0; i < n;) {
  ------------------
  |  Branch (583:30): [True: 1.08k, False: 4]
  ------------------
  584|  1.08k|          int sym = cl.decode(br);
  585|  1.08k|          if (sym < 0) return 0;
  ------------------
  |  Branch (585:15): [True: 0, False: 1.08k]
  ------------------
  586|  1.08k|          if (sym < 16) {
  ------------------
  |  Branch (586:15): [True: 1.03k, False: 49]
  ------------------
  587|  1.03k|            lens[i++] = uint8_t(sym);
  588|  1.03k|          } else if (sym == 16) {
  ------------------
  |  Branch (588:22): [True: 49, False: 0]
  ------------------
  589|     49|            uint32_t rep = br.get(2);
  590|     49|            if (rep == UINT32_MAX) return 0;
  ------------------
  |  Branch (590:17): [True: 0, False: 49]
  ------------------
  591|     49|            rep += 3;
  592|     49|            if (i == 0) return 0;
  ------------------
  |  Branch (592:17): [True: 0, False: 49]
  ------------------
  593|     49|            uint8_t v = lens[i - 1];
  594|    263|            while (rep--) lens[i++] = v;
  ------------------
  |  Branch (594:20): [True: 214, False: 49]
  ------------------
  595|     49|          } else if (sym == 17) {
  ------------------
  |  Branch (595:22): [True: 0, False: 0]
  ------------------
  596|      0|            uint32_t rep = br.get(3);
  597|      0|            if (rep == UINT32_MAX) return 0;
  ------------------
  |  Branch (597:17): [True: 0, False: 0]
  ------------------
  598|      0|            rep += 3;
  599|      0|            while (rep--) lens[i++] = 0;
  ------------------
  |  Branch (599:20): [True: 0, False: 0]
  ------------------
  600|      0|          } else if (sym == 18) {
  ------------------
  |  Branch (600:22): [True: 0, False: 0]
  ------------------
  601|      0|            uint32_t rep = br.get(7);
  602|      0|            if (rep == UINT32_MAX) return 0;
  ------------------
  |  Branch (602:17): [True: 0, False: 0]
  ------------------
  603|      0|            rep += 11;
  604|      0|            while (rep--) lens[i++] = 0;
  ------------------
  |  Branch (604:20): [True: 0, False: 0]
  ------------------
  605|      0|          } else
  606|      0|            return 0;
  607|  1.08k|        }
  608|      4|        if (!lit.build(lens, int(hlit))) return 0;
  ------------------
  |  Branch (608:13): [True: 0, False: 4]
  ------------------
  609|      4|        if (!dist.build(lens + hlit, int(hdist))) return 0;
  ------------------
  |  Branch (609:13): [True: 0, False: 4]
  ------------------
  610|      4|      }
  611|  52.2k|      for (;;) {
  612|  52.2k|        int sym;
  613|  52.2k|        if (btype == 1)
  ------------------
  |  Branch (613:13): [True: 0, False: 52.2k]
  ------------------
  614|      0|          sym = fixed_litlen(br);
  615|  52.2k|        else
  616|  52.2k|          sym = lit.decode(br);
  617|  52.2k|        if (sym < 0) return 0;
  ------------------
  |  Branch (617:13): [True: 0, False: 52.2k]
  ------------------
  618|  52.2k|        if (sym < 256) {
  ------------------
  |  Branch (618:13): [True: 37.2k, False: 14.9k]
  ------------------
  619|  37.2k|          if (out >= dst_cap) return 0;
  ------------------
  |  Branch (619:15): [True: 0, False: 37.2k]
  ------------------
  620|  37.2k|          dst[out++] = uint8_t(sym);
  621|  37.2k|        } else if (sym == 256) {
  ------------------
  |  Branch (621:20): [True: 4, False: 14.9k]
  ------------------
  622|      4|          break;
  623|  14.9k|        } else {
  624|  14.9k|          int len_code = sym - 257;
  625|  14.9k|          if (len_code < 0 || len_code > 28) return 0;
  ------------------
  |  Branch (625:15): [True: 0, False: 14.9k]
  |  Branch (625:31): [True: 0, False: 14.9k]
  ------------------
  626|  14.9k|          uint32_t extra = br.get(len_extra[len_code]);
  627|  14.9k|          if (len_extra[len_code] && extra == UINT32_MAX) return 0;
  ------------------
  |  Branch (627:15): [True: 2.03k, False: 12.9k]
  |  Branch (627:38): [True: 0, False: 2.03k]
  ------------------
  628|  14.9k|          uint32_t length =
  629|  14.9k|              len_base[len_code] + (len_extra[len_code] ? extra : 0);
  ------------------
  |  Branch (629:37): [True: 2.03k, False: 12.9k]
  ------------------
  630|  14.9k|          int dsym;
  631|  14.9k|          if (btype == 1)
  ------------------
  |  Branch (631:15): [True: 0, False: 14.9k]
  ------------------
  632|      0|            dsym = fixed_dist(br);
  633|  14.9k|          else
  634|  14.9k|            dsym = dist.decode(br);
  635|  14.9k|          if (dsym < 0 || dsym > 29) return 0;
  ------------------
  |  Branch (635:15): [True: 0, False: 14.9k]
  |  Branch (635:27): [True: 0, False: 14.9k]
  ------------------
  636|  14.9k|          uint32_t dextra = br.get(dist_extra[dsym]);
  637|  14.9k|          if (dist_extra[dsym] && dextra == UINT32_MAX) return 0;
  ------------------
  |  Branch (637:15): [True: 9.97k, False: 5.01k]
  |  Branch (637:35): [True: 0, False: 9.97k]
  ------------------
  638|  14.9k|          uint32_t distance = dist_base[dsym] + (dist_extra[dsym] ? dextra : 0);
  ------------------
  |  Branch (638:50): [True: 9.97k, False: 5.01k]
  ------------------
  639|  14.9k|          if (distance > out || out + length > dst_cap) return 0;
  ------------------
  |  Branch (639:15): [True: 0, False: 14.9k]
  |  Branch (639:33): [True: 0, False: 14.9k]
  ------------------
  640|   202k|          for (uint32_t k = 0; k < length; k++) {
  ------------------
  |  Branch (640:32): [True: 187k, False: 14.9k]
  ------------------
  641|   187k|            dst[out] = dst[out - distance];
  642|   187k|            out++;
  643|   187k|          }
  644|  14.9k|        }
  645|  52.2k|      }
  646|      4|    } else {
  647|      0|      return 0;  // reserved
  648|      0|    }
  649|      4|    if (bfinal) break;
  ------------------
  |  Branch (649:9): [True: 1, False: 3]
  ------------------
  650|      4|  }
  651|      1|  return out;
  652|      1|}
_ZN3ada4idna7deflate9BitReaderC2EPKhm:
  390|      1|      : p(data), end(data + len) {}
_ZNK3ada4idna7deflate4Huff6decodeERNS1_9BitReaderE:
  464|  68.3k|  int decode(BitReader& br) const {
  465|  68.3k|    if (max_bits == 0) return -1;
  ------------------
  |  Branch (465:9): [True: 0, False: 68.3k]
  ------------------
  466|  68.3k|    int code = 0;
  467|   427k|    for (int len = 1; len <= max_bits; len++) {
  ------------------
  |  Branch (467:23): [True: 427k, False: 0]
  ------------------
  468|   427k|      uint32_t b = br.get(1);
  469|   427k|      if (b == UINT32_MAX) return -1;
  ------------------
  |  Branch (469:11): [True: 0, False: 427k]
  ------------------
  470|   427k|      code = (code << 1) | int(b);
  471|   427k|      int first = first_code[len];
  472|   427k|      int cnt = counts[len];
  473|   427k|      if (cnt && code - first < cnt) {
  ------------------
  |  Branch (473:11): [True: 263k, False: 164k]
  |  Branch (473:18): [True: 68.3k, False: 195k]
  ------------------
  474|  68.3k|        return symbols[base_idx[len] + (code - first)];
  475|  68.3k|      }
  476|   427k|    }
  477|      0|    return -1;
  478|  68.3k|  }
_ZN3ada4idna10crc32_ieeeEPKhm:
 4861|      1|                                         size_t len) noexcept {
 4862|      1|  uint32_t c = 0xFFFFFFFFu;
 4863|   224k|  for (size_t i = 0; i < len; ++i) {
  ------------------
  |  Branch (4863:22): [True: 224k, False: 1]
  ------------------
 4864|   224k|    c ^= data[i];
 4865|  2.02M|    for (int k = 0; k < 8; ++k) {
  ------------------
  |  Branch (4865:21): [True: 1.79M, False: 224k]
  ------------------
 4866|  1.79M|      const uint32_t mask = 0u - (c & 1u);
 4867|  1.79M|      c = (c >> 1) ^ (0xEDB88320u & mask);
 4868|  1.79M|    }
 4869|   224k|  }
 4870|      1|  return ~c;
 4871|      1|}
_ZN3ada4idna6detail33convert_table_blob_to_host_endianEPh:
 4684|      1|inline void convert_table_blob_to_host_endian(uint8_t* buffer) noexcept {
 4685|      1|  if constexpr (std::endian::native == std::endian::little) {
 4686|      1|    (void)buffer;
 4687|      1|    return;
 4688|      1|  }
 4689|      0|  auto u16 = [&](size_t off, size_t count) {
 4690|      1|    bswap_inplace(reinterpret_cast<uint16_t*>(buffer + off), count);
 4691|      1|  };
 4692|      1|  auto u32 = [&](size_t off, size_t count) {
 4693|      1|    bswap_inplace(reinterpret_cast<uint32_t*>(buffer + off), count);
 4694|      1|  };
 4695|      1|  auto u64 = [&](size_t off, size_t count) {
 4696|      1|    bswap_inplace(reinterpret_cast<uint64_t*>(buffer + off), count);
 4697|      1|  };
 4698|       |
 4699|      1|  u16(table_blob::off_idna_stage1, table_blob::count_idna_stage1);
 4700|      1|  u16(table_blob::off_idna_stage2, table_blob::count_idna_stage2);
 4701|      1|  u64(table_blob::off_idna_bool_blocks, table_blob::count_idna_bool_blocks);
 4702|      1|  u16(table_blob::off_decomposition_block,
 4703|      1|      table_blob::count_decomposition_block);
 4704|      1|  u32(table_blob::off_decomposition_data, table_blob::count_decomposition_data);
 4705|      1|  u16(table_blob::off_composition_block, table_blob::count_composition_block);
 4706|      1|  u32(table_blob::off_composition_data, table_blob::count_composition_data);
 4707|      1|  u32(table_blob::off_id_continue_flat, table_blob::count_id_continue_flat);
 4708|      1|  u32(table_blob::off_id_start_flat, table_blob::count_id_start_flat);
 4709|      1|  u32(table_blob::off_dir_start, table_blob::count_dir_start);
 4710|      1|  u32(table_blob::off_dir_final, table_blob::count_dir_final);
 4711|      1|  u32(table_blob::off_combining_flat, table_blob::count_combining_flat);
 4712|      1|}
_ZZN3ada4idna13ensure_tablesEvENKUlmE_clEm:
 4910|     18|    auto at = [&](size_t off) noexcept -> const uint8_t* {
 4911|     18|      return buffer + off;
 4912|     18|    };
can_parse.cc:_ZN3ada4idnaL11idna_lookupEj:
 5065|   720k|static uint16_t idna_lookup(uint32_t cp) noexcept {
 5066|   720k|  if (cp < IDNA_LOW_RANGE_END) {
  ------------------
  |  Branch (5066:7): [True: 718k, False: 2.43k]
  ------------------
 5067|   718k|    uint16_t ref = idna_stage1[cp >> IDNA_BLOCK_BITS];
 5068|   718k|    if (ref & IDNA_BOOL_FLAG) {
  ------------------
  |  Branch (5068:9): [True: 320k, False: 397k]
  ------------------
 5069|   320k|      uint32_t bit_idx =
 5070|   320k|          static_cast<uint32_t>(ref & ~IDNA_BOOL_FLAG) * IDNA_BLOCK_SIZE +
 5071|   320k|          (cp & IDNA_BLOCK_MASK);
 5072|   320k|      bool is_valid = (idna_bool_blocks[bit_idx >> 6] >> (bit_idx & 63u)) & 1u;
 5073|   320k|      return is_valid ? IDNA_VALID : IDNA_DISALLOWED;
  ------------------
  |  Branch (5073:14): [True: 320k, False: 15]
  ------------------
 5074|   320k|    }
 5075|   397k|    return idna_stage2[ref + (cp & IDNA_BLOCK_MASK)];
 5076|   718k|  }
 5077|       |
 5078|  2.43k|  if (cp >= IDNA_HIGH_IGNORED_START && cp < IDNA_HIGH_IGNORED_END) {
  ------------------
  |  Branch (5078:7): [True: 2.33k, False: 105]
  |  Branch (5078:40): [True: 1.97k, False: 357]
  ------------------
 5079|  1.97k|    return IDNA_IGNORED;
 5080|  1.97k|  }
 5081|       |
 5082|    462|  return IDNA_DISALLOWED;
 5083|  2.43k|}
can_parse.cc:_ZN3ada4idnaL21utf8_count_codepointsEPKh:
 5108|   107k|static size_t utf8_count_codepoints(const uint8_t* ptr) noexcept {
 5109|   107k|  size_t n = 0;
 5110|   623k|  while (*ptr != 0) {
  ------------------
  |  Branch (5110:10): [True: 516k, False: 107k]
  ------------------
 5111|   516k|    ++n;
 5112|   516k|    if (*ptr < 0x80u) {
  ------------------
  |  Branch (5112:9): [True: 127k, False: 388k]
  ------------------
 5113|   127k|      ++ptr;
 5114|   388k|    } else if (*ptr < 0xE0u) {
  ------------------
  |  Branch (5114:16): [True: 261k, False: 127k]
  ------------------
 5115|   261k|      ptr += 2;
 5116|   261k|    } else if (*ptr < 0xF0u) {
  ------------------
  |  Branch (5116:16): [True: 125k, False: 1.91k]
  ------------------
 5117|   125k|      ptr += 3;
 5118|   125k|    } else {
 5119|  1.91k|      ptr += 4;
 5120|  1.91k|    }
 5121|   516k|  }
 5122|   107k|  return n;
 5123|   107k|}
can_parse.cc:_ZN3ada4idnaL9utf8_nextERPKh:
 5086|   514k|static char32_t utf8_next(const uint8_t*& ptr) noexcept {
 5087|   514k|  uint8_t b0 = *ptr++;
 5088|   514k|  if (b0 < 0x80u) return static_cast<char32_t>(b0);
  ------------------
  |  Branch (5088:7): [True: 127k, False: 387k]
  ------------------
 5089|   387k|  if (b0 < 0xE0u) {
  ------------------
  |  Branch (5089:7): [True: 261k, False: 125k]
  ------------------
 5090|   261k|    uint32_t cp = (b0 & 0x1Fu) << 6;
 5091|   261k|    cp |= (*ptr++ & 0x3Fu);
 5092|   261k|    return static_cast<char32_t>(cp);
 5093|   261k|  }
 5094|   125k|  if (b0 < 0xF0u) {
  ------------------
  |  Branch (5094:7): [True: 124k, False: 1.91k]
  ------------------
 5095|   124k|    uint32_t cp = (b0 & 0x0Fu) << 12;
 5096|   124k|    cp |= ((*ptr++ & 0x3Fu) << 6);
 5097|   124k|    cp |= (*ptr++ & 0x3Fu);
 5098|   124k|    return static_cast<char32_t>(cp);
 5099|   124k|  }
 5100|  1.91k|  uint32_t cp = (b0 & 0x07u) << 18;
 5101|  1.91k|  cp |= ((*ptr++ & 0x3Fu) << 12);
 5102|  1.91k|  cp |= ((*ptr++ & 0x3Fu) << 6);
 5103|  1.91k|  cp |= (*ptr++ & 0x3Fu);
 5104|  1.91k|  return static_cast<char32_t>(cp);
 5105|   125k|}
_ZN3ada4idna23decomposition_block_rowEh:
 4982|  1.29M|inline const uint16_t* decomposition_block_row(uint8_t bi) noexcept {
 4983|  1.29M|  if (bi >= kDecompBlockRows) {
  ------------------
  |  Branch (4983:7): [True: 0, False: 1.29M]
  ------------------
 4984|      0|    bi = 0;
 4985|      0|  }
 4986|  1.29M|  return decomposition_block_flat + static_cast<size_t>(bi) * kDecompBlockCols;
 4987|  1.29M|}
_ZN3ada4idna13ccc_block_rowEh:
 4989|  1.87M|inline const uint8_t* ccc_block_row(uint8_t bi) noexcept {
 4990|  1.87M|  if (bi >= kCccBlockRows) {
  ------------------
  |  Branch (4990:7): [True: 0, False: 1.87M]
  ------------------
 4991|      0|    bi = 0;
 4992|      0|  }
 4993|  1.87M|  return ccc_block_flat + static_cast<size_t>(bi) * kCccBlockCols;
 4994|  1.87M|}
_ZN3ada4idna16tables_are_readyEv:
 4873|  23.2k|[[nodiscard]] inline bool tables_are_ready() noexcept {
 4874|  23.2k|  return tables_init_state.load(std::memory_order_acquire) == kTablesReady;
 4875|  23.2k|}
can_parse.cc:_ZN3ada4idnaL23canonical_decomp_lengthEDi:
 5265|  1.17M|static size_t canonical_decomp_length(char32_t current_character) noexcept {
 5266|  1.17M|  if (current_character >= hangul_sbase &&
  ------------------
  |  Branch (5266:7): [True: 48.4k, False: 1.12M]
  ------------------
 5267|  48.4k|      current_character < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5267:7): [True: 27.5k, False: 20.8k]
  ------------------
 5268|       |    // Hangul precomposed syllables are NFC.
 5269|  27.5k|    return 0;
 5270|  27.5k|  }
 5271|  1.14M|  if (current_character >= 0x110000) {
  ------------------
  |  Branch (5271:7): [True: 0, False: 1.14M]
  ------------------
 5272|      0|    return 0;
 5273|      0|  }
 5274|  1.14M|  const uint8_t di = decomposition_index[current_character >> 8];
 5275|  1.14M|  const uint16_t* const decomposition =
 5276|  1.14M|      decomposition_block_row(di) + (current_character % 256);
 5277|  1.14M|  size_t decomposition_length =
 5278|  1.14M|      (decomposition[1] >> 2) - (decomposition[0] >> 2);
 5279|  1.14M|  if ((decomposition_length > 0) && (decomposition[0] & 1)) {
  ------------------
  |  Branch (5279:7): [True: 41.7k, False: 1.10M]
  |  Branch (5279:37): [True: 0, False: 41.7k]
  ------------------
 5280|      0|    decomposition_length = 0;  // compatibility-only: ignored for NFC
 5281|      0|  }
 5282|  1.14M|  return decomposition_length;
 5283|  1.14M|}
can_parse.cc:_ZN3ada4idnaL12trailing_cccEDi:
 5466|   576k|static uint8_t trailing_ccc(char32_t c) noexcept {
 5467|   576k|  const size_t length = canonical_decomp_length(c);
 5468|   576k|  if (length == 0) {
  ------------------
  |  Branch (5468:7): [True: 556k, False: 19.9k]
  ------------------
 5469|   556k|    return get_ccc(c);
 5470|   556k|  }
 5471|  19.9k|  const uint16_t* const decomposition =
 5472|  19.9k|      decomposition_block_row(decomposition_index[c >> 8]) + (c % 256);
 5473|  19.9k|  const size_t base = decomposition[0] >> 2;
 5474|  19.9k|  return get_ccc(decomposition_data[base + length - 1]);
 5475|   576k|}
can_parse.cc:_ZN3ada4idnaL13would_composeENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5397|  13.4k|static bool would_compose(std::u32string_view input) noexcept {
 5398|   522k|  for (size_t input_count = 0; input_count < input.size();) {
  ------------------
  |  Branch (5398:32): [True: 512k, False: 10.4k]
  ------------------
 5399|   512k|    const char32_t current = input[input_count];
 5400|   512k|    if (current >= hangul_lbase && current < hangul_lbase + hangul_lcount) {
  ------------------
  |  Branch (5400:9): [True: 151k, False: 360k]
  |  Branch (5400:36): [True: 5.04k, False: 146k]
  ------------------
 5401|  5.04k|      if (input_count + 1 < input.size() &&
  ------------------
  |  Branch (5401:11): [True: 4.82k, False: 216]
  ------------------
 5402|  4.82k|          input[input_count + 1] >= hangul_vbase &&
  ------------------
  |  Branch (5402:11): [True: 1.67k, False: 3.15k]
  ------------------
 5403|  1.67k|          input[input_count + 1] < hangul_vbase + hangul_vcount) {
  ------------------
  |  Branch (5403:11): [True: 294, False: 1.37k]
  ------------------
 5404|    294|        return true;
 5405|    294|      }
 5406|  4.74k|      ++input_count;
 5407|  4.74k|      continue;
 5408|  5.04k|    }
 5409|   507k|    if (current >= hangul_sbase && current < hangul_sbase + hangul_scount) {
  ------------------
  |  Branch (5409:9): [True: 11.0k, False: 496k]
  |  Branch (5409:36): [True: 6.36k, False: 4.65k]
  ------------------
 5410|  6.36k|      if ((current - hangul_sbase) % hangul_tcount &&
  ------------------
  |  Branch (5410:11): [True: 4.60k, False: 1.76k]
  ------------------
 5411|  4.60k|          input_count + 1 < input.size() &&
  ------------------
  |  Branch (5411:11): [True: 4.25k, False: 351]
  ------------------
 5412|  4.25k|          input[input_count + 1] > hangul_tbase &&
  ------------------
  |  Branch (5412:11): [True: 3.40k, False: 852]
  ------------------
 5413|  3.40k|          input[input_count + 1] < hangul_tbase + hangul_tcount) {
  ------------------
  |  Branch (5413:11): [True: 474, False: 2.93k]
  ------------------
 5414|    474|        return true;
 5415|    474|      }
 5416|  5.89k|      ++input_count;
 5417|  5.89k|      continue;
 5418|  6.36k|    }
 5419|   500k|    if (current < 0x110000) {
  ------------------
  |  Branch (5419:9): [True: 500k, False: 0]
  ------------------
 5420|   500k|      const uint16_t* composition =
 5421|   500k|          composition_block_row(composition_index[current >> 8]) +
 5422|   500k|          (current % 256);
 5423|   500k|      int32_t previous_ccc = -1;
 5424|   500k|      size_t j = input_count;
 5425|   508k|      for (; j + 1 < input.size(); ++j) {
  ------------------
  |  Branch (5425:14): [True: 498k, False: 9.85k]
  ------------------
 5426|   498k|        uint8_t ccc = get_ccc(input[j + 1]);
 5427|   498k|        if (composition[1] != composition[0] && previous_ccc < ccc) {
  ------------------
  |  Branch (5427:13): [True: 112k, False: 386k]
  |  Branch (5427:49): [True: 108k, False: 3.98k]
  ------------------
 5428|   108k|          int left = composition[0];
 5429|   108k|          int right = composition[1];
 5430|   108k|          if (left < 0 || right < left ||
  ------------------
  |  Branch (5430:15): [True: 0, False: 108k]
  |  Branch (5430:27): [True: 0, False: 108k]
  ------------------
 5431|   108k|              static_cast<size_t>(right) > composition_data_size) {
  ------------------
  |  Branch (5431:15): [True: 0, False: 108k]
  ------------------
 5432|      0|            break;
 5433|      0|          }
 5434|   278k|          while (left + 2 < right) {
  ------------------
  |  Branch (5434:18): [True: 170k, False: 108k]
  ------------------
 5435|   170k|            int middle = left + (((right - left) >> 1) & ~1);
 5436|   170k|            if (composition_data[static_cast<size_t>(middle)] <= input[j + 1]) {
  ------------------
  |  Branch (5436:17): [True: 11.0k, False: 159k]
  ------------------
 5437|  11.0k|              left = middle;
 5438|  11.0k|            }
 5439|   170k|            if (composition_data[static_cast<size_t>(middle)] >= input[j + 1]) {
  ------------------
  |  Branch (5439:17): [True: 161k, False: 9.43k]
  ------------------
 5440|   161k|              right = middle;
 5441|   161k|            }
 5442|   170k|          }
 5443|   108k|          if (static_cast<size_t>(left + 1) < composition_data_size &&
  ------------------
  |  Branch (5443:15): [True: 108k, False: 0]
  ------------------
 5444|   108k|              composition_data[static_cast<size_t>(left)] == input[j + 1]) {
  ------------------
  |  Branch (5444:15): [True: 2.20k, False: 105k]
  ------------------
 5445|  2.20k|            return true;
 5446|  2.20k|          }
 5447|   108k|        }
 5448|   496k|        if (ccc == 0) {
  ------------------
  |  Branch (5448:13): [True: 488k, False: 7.74k]
  ------------------
 5449|   488k|          break;
 5450|   488k|        }
 5451|  7.74k|        previous_ccc = ccc;
 5452|  7.74k|      }
 5453|   498k|      input_count = j + 1;
 5454|   498k|      continue;
 5455|   500k|    }
 5456|      0|    ++input_count;
 5457|      0|  }
 5458|  10.4k|  return false;
 5459|  13.4k|}
_ZN3ada4idna21composition_block_rowEh:
 4996|   576k|inline const uint16_t* composition_block_row(uint8_t bi) noexcept {
 4997|   576k|  if (bi >= kCompBlockRows) {
  ------------------
  |  Branch (4997:7): [True: 0, False: 576k]
  ------------------
 4998|      0|    bi = 0;
 4999|      0|  }
 5000|   576k|  return composition_block_flat + static_cast<size_t>(bi) * kCompBlockCols;
 5001|   576k|}
can_parse.cc:_ZN3ada4idnaL19char_to_digit_valueEc:
 5643|  95.5k|static constexpr int32_t char_to_digit_value(char value) {
 5644|  95.5k|  if (value >= 'a' && value <= 'z') return value - 'a';
  ------------------
  |  Branch (5644:7): [True: 83.4k, False: 12.1k]
  |  Branch (5644:23): [True: 83.3k, False: 15]
  ------------------
 5645|  12.1k|  if (value >= '0' && value <= '9') return value - '0' + 26;
  ------------------
  |  Branch (5645:7): [True: 12.0k, False: 51]
  |  Branch (5645:23): [True: 12.0k, False: 21]
  ------------------
 5646|     72|  return -1;
 5647|  12.1k|}
can_parse.cc:_ZN3ada4idnaL5adaptEiib:
 5653|   276k|static constexpr int32_t adapt(int32_t d, int32_t n, bool firsttime) {
 5654|   276k|  if (firsttime) {
  ------------------
  |  Branch (5654:7): [True: 45.8k, False: 230k]
  ------------------
 5655|  45.8k|    d = d / damp;
 5656|   230k|  } else {
 5657|   230k|    d = d / 2;
 5658|   230k|  }
 5659|   276k|  d += d / n;
 5660|   276k|  int32_t k = 0;
 5661|   373k|  while (d > ((base - tmin) * tmax) / 2) {
  ------------------
  |  Branch (5661:10): [True: 97.0k, False: 276k]
  ------------------
 5662|  97.0k|    d /= base - tmin;
 5663|  97.0k|    k += base;
 5664|  97.0k|  }
 5665|   276k|  return k + (((base - tmin + 1) * d) / (d + skew));
 5666|   276k|}
can_parse.cc:_ZN3ada4idnaL13digit_to_charEi:
 5649|   505k|static constexpr char digit_to_char(int32_t digit) {
 5650|   505k|  return digit < 26 ? char(digit + 97) : char(digit + 22);
  ------------------
  |  Branch (5650:10): [True: 360k, False: 145k]
  ------------------
 5651|   505k|}
can_parse.cc:_ZZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEEENK3$_0clIA2_jEEDaRKT_:
 5988|   382k|      comb_span, label.front(), {}, [](const auto& range) { return range[1]; });
can_parse.cc:_ZZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEEENK3$_1clEj:
 6073|  5.28k|      auto is_l_or_d = [](uint32_t code) {
 6074|  5.28k|        return std::ranges::binary_search(L, code) ||
  ------------------
  |  Branch (6074:16): [True: 558, False: 4.72k]
  ------------------
 6075|  4.72k|               std::ranges::binary_search(D, code);
  ------------------
  |  Branch (6075:16): [True: 1.27k, False: 3.45k]
  ------------------
 6076|  5.28k|      };
can_parse.cc:_ZZN3ada4idna14is_label_validENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEEENK3$_2clEj:
 6077|  6.82k|      auto is_r_or_d = [](uint32_t code) {
 6078|  6.82k|        return std::ranges::binary_search(R, code) ||
  ------------------
  |  Branch (6078:16): [True: 1.09k, False: 5.73k]
  ------------------
 6079|  5.73k|               std::ranges::binary_search(D, code);
  ------------------
  |  Branch (6079:16): [True: 456, False: 5.27k]
  ------------------
 6080|  6.82k|      };
can_parse.cc:_ZN3ada4idnaL20find_last_not_of_nsmENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5935|  43.1k|    const std::u32string_view label) noexcept {
 5936|  51.3k|  for (int i = static_cast<int>(label.size() - 1); i >= 0; i--)
  ------------------
  |  Branch (5936:52): [True: 51.3k, False: 0]
  ------------------
 5937|  51.3k|    if (find_direction(label[i]) != direction::NSM) return i;
  ------------------
  |  Branch (5937:9): [True: 43.1k, False: 8.15k]
  ------------------
 5938|       |
 5939|      0|  return std::u32string_view::npos;
 5940|  43.1k|}
can_parse.cc:_ZN3ada4idnaL12is_rtl_labelENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 5944|  43.1k|inline static bool is_rtl_label(const std::u32string_view label) noexcept {
 5945|  43.1k|  const size_t mask =
 5946|  43.1k|      (1u << direction::R) | (1u << direction::AL) | (1u << direction::AN);
 5947|       |
 5948|  43.1k|  size_t directions = 0;
 5949|   289k|  for (size_t i = 0; i < label.size(); i++) {
  ------------------
  |  Branch (5949:22): [True: 246k, False: 43.1k]
  ------------------
 5950|   246k|    directions |= 1u << find_direction(label[i]);
 5951|   246k|  }
 5952|  43.1k|  return (directions & mask) != 0;
 5953|  43.1k|}
can_parse.cc:_ZN3ada4idnaL14find_directionEj:
 5916|   346k|inline static direction find_direction(uint32_t code_point) noexcept {
 5917|       |  // Binary search on dir_final (sorted upper bounds).
 5918|   346k|  size_t lo = 0, hi = dir_table_count;
 5919|  3.92M|  while (lo < hi) {
  ------------------
  |  Branch (5919:10): [True: 3.57M, False: 346k]
  ------------------
 5920|  3.57M|    size_t mid = lo + (hi - lo) / 2;
 5921|  3.57M|    if (dir_final[mid] < code_point) {
  ------------------
  |  Branch (5921:9): [True: 1.50M, False: 2.07M]
  ------------------
 5922|  1.50M|      lo = mid + 1;
 5923|  2.07M|    } else {
 5924|  2.07M|      hi = mid;
 5925|  2.07M|    }
 5926|  3.57M|  }
 5927|   346k|  if (lo == dir_table_count) {
  ------------------
  |  Branch (5927:7): [True: 0, False: 346k]
  ------------------
 5928|      0|    return direction::NONE;
 5929|      0|  }
 5930|   346k|  return code_point >= dir_start[lo] ? static_cast<direction>(dir_value[lo])
  ------------------
  |  Branch (5930:10): [True: 342k, False: 4.61k]
  ------------------
 5931|   346k|                                     : direction::NONE;
 5932|   346k|}
_ZN3ada4idna8is_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6301|  19.7k|bool constexpr is_ascii(std::string_view view) {
 6302|   496k|  for (uint8_t c : view) {
  ------------------
  |  Branch (6302:18): [True: 496k, False: 6.09k]
  ------------------
 6303|   496k|    if (c >= 0x80) {
  ------------------
  |  Branch (6303:9): [True: 13.6k, False: 482k]
  ------------------
 6304|  13.6k|      return false;
 6305|  13.6k|    }
 6306|   496k|  }
 6307|  6.09k|  return true;
 6308|  19.7k|}
can_parse.cc:_ZN3ada4idnaL19from_ascii_to_asciiENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 6338|  6.09k|static void from_ascii_to_ascii(std::string_view ut8_string, std::string& out) {
 6339|  6.09k|  out.assign(ut8_string);
 6340|  6.09k|  ascii_map(out.data(), out.size());
 6341|  6.09k|}
_ZN3ada4idna8is_asciiENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6292|  73.3k|bool constexpr is_ascii(std::u32string_view view) {
 6293|   259k|  for (uint32_t c : view) {
  ------------------
  |  Branch (6293:19): [True: 259k, False: 8.88k]
  ------------------
 6294|   259k|    if (c >= 0x80) {
  ------------------
  |  Branch (6294:9): [True: 64.4k, False: 195k]
  ------------------
 6295|  64.4k|      return false;
 6296|  64.4k|    }
 6297|   259k|  }
 6298|  8.88k|  return true;
 6299|  73.3k|}
can_parse.cc:_ZN3ada4idnaL13is_ace_prefixENSt3__117basic_string_viewIDiNS1_11char_traitsIDiEEEE:
 6354|  53.9k|static bool is_ace_prefix(std::u32string_view label) noexcept {
 6355|  53.9k|  return label.size() >= 4 && label[0] == U'x' && label[1] == U'n' &&
  ------------------
  |  Branch (6355:10): [True: 40.2k, False: 13.6k]
  |  Branch (6355:31): [True: 10.5k, False: 29.7k]
  |  Branch (6355:51): [True: 9.78k, False: 756]
  ------------------
 6356|  9.78k|         label[2] == U'-' && label[3] == U'-';
  ------------------
  |  Branch (6356:10): [True: 9.26k, False: 525]
  |  Branch (6356:30): [True: 8.79k, False: 474]
  ------------------
 6357|  53.9k|}
can_parse.cc:_ZN3ada4idnaL18append_ascii_labelERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS1_17basic_string_viewIDiNS3_IDiEEEE:
 6344|  14.3k|static void append_ascii_label(std::string& out, std::u32string_view label) {
 6345|  14.3k|  const size_t old = out.size();
 6346|  14.3k|  out.resize(old + label.size());
 6347|  14.3k|  char* dest = out.data() + old;
 6348|   180k|  for (char32_t c : label) {
  ------------------
  |  Branch (6348:19): [True: 180k, False: 14.3k]
  ------------------
 6349|   180k|    *dest++ = static_cast<char>(c);
 6350|   180k|  }
 6351|  14.3k|}
_ZZN3ada7unicode14percent_encodeILb1EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEEENKUlcE_clEc:
 7358|  13.4k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7359|  13.4k|    return character_sets::bit_at(character_set, c);
 7360|  13.4k|  });
_ZZN3ada7unicode14percent_encodeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKhRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEEENKUlcE_clEc:
 7358|  43.3k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7359|  43.3k|    return character_sets::bit_at(character_set, c);
 7360|  43.3k|  });
_ZN3ada7unicode18is_ascii_hex_digitEc:
 7092|  24.2k|ada_really_inline constexpr bool is_ascii_hex_digit(const char c) noexcept {
 7093|  24.2k|  return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') ||
  ------------------
  |  Branch (7093:11): [True: 15.2k, False: 9.06k]
  |  Branch (7093:23): [True: 9.33k, False: 5.88k]
  |  Branch (7093:37): [True: 5.85k, False: 9.08k]
  |  Branch (7093:49): [True: 2.86k, False: 2.99k]
  ------------------
 7094|  12.0k|         (c >= 'a' && c <= 'f');
  ------------------
  |  Branch (7094:11): [True: 2.88k, False: 9.19k]
  |  Branch (7094:23): [True: 2.67k, False: 210]
  ------------------
 7095|  24.2k|}
_ZN3ada7unicode21convert_hex_to_binaryEc:
 7182|  12.2k|unsigned constexpr convert_hex_to_binary(const char c) noexcept {
 7183|  12.2k|  return hex_to_binary_table[c - '0'];
 7184|  12.2k|}
can_parse.cc:_ZZN3ada7unicode14percent_encodeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKhENK3$_0clEc:
 7326|   108k|  auto pointer = std::ranges::find_if(input, [character_set](const char c) {
 7327|   108k|    return character_sets::bit_at(character_set, c);
 7328|   108k|  });
_ZN3ada7unicode36contains_forbidden_domain_code_pointEPKcm:
 7019|  14.6k|    const char* input, size_t length) noexcept {
 7020|  14.6k|  size_t i = 0;
 7021|  14.6k|  uint8_t accumulator{};
 7022|   315k|  for (; i + 4 <= length; i += 4) {
  ------------------
  |  Branch (7022:10): [True: 301k, False: 14.6k]
  ------------------
 7023|   301k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i])];
 7024|   301k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 1])];
 7025|   301k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 2])];
 7026|   301k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i + 3])];
 7027|   301k|  }
 7028|  37.1k|  for (; i < length; i++) {
  ------------------
  |  Branch (7028:10): [True: 22.5k, False: 14.6k]
  ------------------
 7029|  22.5k|    accumulator |= is_forbidden_domain_code_point_table[uint8_t(input[i])];
 7030|  22.5k|  }
 7031|  14.6k|  return accumulator;
 7032|  14.6k|}
can_parse.cc:_ZN3ada7unicode12_GLOBAL__N_122percent_encode_to_wideEPKcS3_PKhRNSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEE:
 7708|    880|                            const uint8_t character_set[], std::string& out) {
 7709|       |  // Worst case every byte becomes %XX. Avoids realloc while walking windows.
 7710|    880|  out.reserve(out.size() + static_cast<size_t>(end - p) * 3);
 7711|    880|#if ADA_UNICODE_SSSE3
 7712|    880|  const ssse3_percent_tables tables = load_ssse3_percent_tables(character_set);
 7713|    880|  percent_encode_to_ssse3(p, end, character_set, tables, out);
 7714|       |#elif ADA_NEON
 7715|       |  percent_encode_to_neon(p, end, character_set,
 7716|       |                         load_neon_percent_table(character_set), out);
 7717|       |#elif ADA_RVV
 7718|       |  percent_encode_to_rvv(p, end, character_set, out);
 7719|       |#endif
 7720|    880|}
can_parse.cc:_ZN3ada7unicode12_GLOBAL__N_125load_ssse3_percent_tablesEPKh:
 7535|    880|load_ssse3_percent_tables(const uint8_t character_set[]) noexcept {
 7536|    880|  ssse3_percent_tables t{};
 7537|    880|  t.cs_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(character_set));
 7538|    880|  t.cs_hi =
 7539|    880|      _mm_loadu_si128(reinterpret_cast<const __m128i*>(character_set + 16));
 7540|       |  // 1 << (0..7), duplicated so pshufb(index & 7) works for every lane.
 7541|    880|  t.pow2 =
 7542|    880|      _mm_setr_epi8(1, 2, 4, 8, 16, 32, 64, -128, 1, 2, 4, 8, 16, 32, 64, -128);
 7543|    880|  t.mask_0f = _mm_set1_epi8(0x0F);
 7544|    880|  t.mask_07 = _mm_set1_epi8(0x07);
 7545|    880|  t.zero = _mm_setzero_si128();
 7546|    880|  return t;
 7547|    880|}
can_parse.cc:_ZN3ada7unicode12_GLOBAL__N_123percent_encode_to_ssse3EPKcS3_PKhRKNS1_20ssse3_percent_tablesERNSt3__112basic_stringIcNS9_11char_traitsIcEENS9_9allocatorIcEEEE:
 7566|    880|    const ssse3_percent_tables& tables, std::string& out) {
 7567|       |  // Pair 16-byte windows so a fully clean 32-byte run is one append.
 7568|  2.83k|  while (p + 32 <= end) {
  ------------------
  |  Branch (7568:10): [True: 1.95k, False: 880]
  ------------------
 7569|  1.95k|    const __m128i word0 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
 7570|  1.95k|    const __m128i word1 =
 7571|  1.95k|        _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
 7572|  1.95k|    const int mask0 = ssse3_percent_mask(word0, tables);
 7573|  1.95k|    const int mask1 = ssse3_percent_mask(word1, tables);
 7574|  1.95k|    if ((mask0 | mask1) == 0) {
  ------------------
  |  Branch (7574:9): [True: 123, False: 1.83k]
  ------------------
 7575|    123|      out.append(p, 32);
 7576|  1.83k|    } else {
 7577|  1.83k|      if (mask0 == 0) {
  ------------------
  |  Branch (7577:11): [True: 100, False: 1.73k]
  ------------------
 7578|    100|        out.append(p, 16);
 7579|  1.73k|      } else {
 7580|  1.73k|        encode_mask_window(p, static_cast<uint32_t>(mask0), 16, out);
 7581|  1.73k|      }
 7582|  1.83k|      if (mask1 == 0) {
  ------------------
  |  Branch (7582:11): [True: 184, False: 1.64k]
  ------------------
 7583|    184|        out.append(p + 16, 16);
 7584|  1.64k|      } else {
 7585|  1.64k|        encode_mask_window(p + 16, static_cast<uint32_t>(mask1), 16, out);
 7586|  1.64k|      }
 7587|  1.83k|    }
 7588|  1.95k|    p += 32;
 7589|  1.95k|  }
 7590|    880|  if (p + 16 <= end) {
  ------------------
  |  Branch (7590:7): [True: 600, False: 280]
  ------------------
 7591|    600|    const __m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
 7592|    600|    const int mask = ssse3_percent_mask(word, tables);
 7593|    600|    if (mask == 0) {
  ------------------
  |  Branch (7593:9): [True: 63, False: 537]
  ------------------
 7594|     63|      out.append(p, 16);
 7595|    537|    } else {
 7596|    537|      encode_mask_window(p, static_cast<uint32_t>(mask), 16, out);
 7597|    537|    }
 7598|    600|    p += 16;
 7599|    600|  }
 7600|    880|  percent_encode_to_scalar(p, end, character_set, out);
 7601|    880|}
can_parse.cc:_ZN3ada7unicode12_GLOBAL__N_118ssse3_percent_maskEDv2_xRKNS1_20ssse3_percent_tablesE:
 7550|  4.51k|    __m128i word, const ssse3_percent_tables& tables) noexcept {
 7551|  4.51k|  const __m128i idx = _mm_and_si128(_mm_srli_epi16(word, 3), tables.mask_0f);
 7552|  4.51k|  const __m128i lo = _mm_shuffle_epi8(tables.cs_lo, idx);
 7553|  4.51k|  const __m128i hi = _mm_shuffle_epi8(tables.cs_hi, idx);
 7554|       |  // Bytes 0x80-0xFF are signed-negative; select the high half of the bitmap.
 7555|  4.51k|  const __m128i high_byte = _mm_cmpgt_epi8(tables.zero, word);
 7556|  4.51k|  const __m128i cs_byte = _mm_or_si128(_mm_and_si128(hi, high_byte),
 7557|  4.51k|                                       _mm_andnot_si128(high_byte, lo));
 7558|  4.51k|  const __m128i bits =
 7559|  4.51k|      _mm_shuffle_epi8(tables.pow2, _mm_and_si128(word, tables.mask_07));
 7560|  4.51k|  const __m128i hits = _mm_and_si128(cs_byte, bits);
 7561|  4.51k|  return _mm_movemask_epi8(_mm_cmpeq_epi8(hits, tables.zero)) ^ 0xFFFF;
 7562|  4.51k|}
can_parse.cc:_ZN3ada7unicode12_GLOBAL__N_118encode_mask_windowEPKcjmRNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEE:
 7490|  3.91k|                                          size_t width, std::string& out) {
 7491|  3.91k|  uint64_t bits = mask;
 7492|  3.91k|  size_t off = 0;
 7493|  52.5k|  while (bits != 0) {
  ------------------
  |  Branch (7493:10): [True: 48.6k, False: 3.91k]
  ------------------
 7494|  48.6k|    const int zero_run = trailing_zeroes32(static_cast<uint32_t>(bits));
 7495|  48.6k|    if (zero_run != 0) {
  ------------------
  |  Branch (7495:9): [True: 2.99k, False: 45.6k]
  ------------------
 7496|  2.99k|      out.append(p + off, static_cast<size_t>(zero_run));
 7497|  2.99k|    }
 7498|  48.6k|    off += static_cast<size_t>(zero_run);
 7499|  48.6k|    out.append(character_sets::hex + uint8_t(p[off]) * 4, 3);
 7500|  48.6k|    ++off;
 7501|  48.6k|    bits >>= static_cast<unsigned>(zero_run + 1);
 7502|  48.6k|  }
 7503|  3.91k|  if (off < width) {
  ------------------
  |  Branch (7503:7): [True: 970, False: 2.94k]
  ------------------
 7504|    970|    out.append(p + off, width - off);
 7505|    970|  }
 7506|  3.91k|}
can_parse.cc:_ZN3ada7unicode12_GLOBAL__N_117trailing_zeroes32Ej:
 7478|  48.6k|ada_really_inline int trailing_zeroes32(uint32_t input_num) noexcept {
 7479|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
 7480|       |  unsigned long ret;
 7481|       |  _BitScanForward(&ret, input_num);
 7482|       |  return static_cast<int>(ret);
 7483|       |#else
 7484|  48.6k|  return __builtin_ctz(input_num);
 7485|  48.6k|#endif
 7486|  48.6k|}
can_parse.cc:_ZN3ada7unicode12_GLOBAL__N_124percent_encode_to_scalarEPKcS3_PKhRNSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEE:
 7511|    880|                                                std::string& out) {
 7512|  3.51k|  for (; p != end; ++p) {
  ------------------
  |  Branch (7512:10): [True: 2.63k, False: 880]
  ------------------
 7513|  2.63k|    if (character_sets::bit_at(character_set, *p)) {
  ------------------
  |  Branch (7513:9): [True: 2.05k, False: 578]
  ------------------
 7514|  2.05k|      out.append(character_sets::hex + uint8_t(*p) * 4, 3);
 7515|  2.05k|    } else {
 7516|    578|      out += *p;
 7517|    578|    }
 7518|  2.63k|  }
 7519|    880|}
can_parse.cc:_ZN3ada11serializers12_GLOBAL__N_113write_hex_u16EPct:
 7775|  2.02k|ada_really_inline char* write_hex_u16(char* p, uint16_t v) noexcept {
 7776|  2.02k|  static constexpr char kHex[] = "0123456789abcdef";
 7777|  2.02k|  if (v >= 0x1000) {
  ------------------
  |  Branch (7777:7): [True: 219, False: 1.80k]
  ------------------
 7778|    219|    *p++ = kHex[(v >> 12) & 0xf];
 7779|    219|    *p++ = kHex[(v >> 8) & 0xf];
 7780|    219|    *p++ = kHex[(v >> 4) & 0xf];
 7781|    219|    *p++ = kHex[v & 0xf];
 7782|  1.80k|  } else if (v >= 0x100) {
  ------------------
  |  Branch (7782:14): [True: 282, False: 1.52k]
  ------------------
 7783|    282|    *p++ = kHex[(v >> 8) & 0xf];
 7784|    282|    *p++ = kHex[(v >> 4) & 0xf];
 7785|    282|    *p++ = kHex[v & 0xf];
 7786|  1.52k|  } else if (v >= 0x10) {
  ------------------
  |  Branch (7786:14): [True: 243, False: 1.28k]
  ------------------
 7787|    243|    *p++ = kHex[(v >> 4) & 0xf];
 7788|    243|    *p++ = kHex[v & 0xf];
 7789|  1.28k|  } else {
 7790|  1.28k|    *p++ = kHex[v & 0xf];
 7791|  1.28k|  }
 7792|  2.02k|  return p;
 7793|  2.02k|}
can_parse.cc:_ZN3ada11serializers12_GLOBAL__N_18write_u8EPch:
 7761|  16.2k|ada_really_inline char* write_u8(char* p, uint8_t v) noexcept {
 7762|  16.2k|  if (v < 10) {
  ------------------
  |  Branch (7762:7): [True: 13.3k, False: 2.92k]
  ------------------
 7763|  13.3k|    *p++ = static_cast<char>('0' + v);
 7764|  13.3k|  } else if (v < 100) {
  ------------------
  |  Branch (7764:14): [True: 1.22k, False: 1.70k]
  ------------------
 7765|  1.22k|    std::memcpy(p, &digit_pairs[static_cast<size_t>(v) * 2], 2);
 7766|  1.22k|    p += 2;
 7767|  1.70k|  } else {
 7768|  1.70k|    *p++ = static_cast<char>('0' + v / 100);
 7769|  1.70k|    std::memcpy(p, &digit_pairs[static_cast<size_t>(v % 100) * 2], 2);
 7770|  1.70k|    p += 2;
 7771|  1.70k|  }
 7772|  16.2k|  return p;
 7773|  16.2k|}
_ZN3ada7unicode19has_tabs_or_newlineENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6868|   121k|    std::string_view user_input) noexcept {
 6869|       |  // first check for short strings in which case we do it naively.
 6870|   121k|  if (user_input.size() < 16) {  // slow path
  ------------------
  |  Branch (6870:7): [True: 92.4k, False: 28.7k]
  ------------------
 6871|  92.4k|    return std::ranges::any_of(user_input, is_tabs_or_newline);
 6872|  92.4k|  }
 6873|       |  // fast path for long strings (expected to be common)
 6874|  28.7k|  size_t i = 0;
 6875|  28.7k|  const __m128i mask1 = _mm_set1_epi8('\r');
 6876|  28.7k|  const __m128i mask2 = _mm_set1_epi8('\n');
 6877|  28.7k|  const __m128i mask3 = _mm_set1_epi8('\t');
 6878|       |  // If we supported SSSE3, we could use the algorithm that we use for NEON.
 6879|  28.7k|  __m128i running{0};
 6880|   155k|  for (; i + 15 < user_input.size(); i += 16) {
  ------------------
  |  Branch (6880:10): [True: 127k, False: 28.7k]
  ------------------
 6881|   127k|    __m128i word = _mm_loadu_si128((const __m128i*)(user_input.data() + i));
 6882|   127k|    running = _mm_or_si128(
 6883|   127k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
 6884|   127k|                                           _mm_cmpeq_epi8(word, mask2))),
 6885|   127k|        _mm_cmpeq_epi8(word, mask3));
 6886|   127k|  }
 6887|  28.7k|  if (i < user_input.size()) {
  ------------------
  |  Branch (6887:7): [True: 25.5k, False: 3.27k]
  ------------------
 6888|  25.5k|    __m128i word = _mm_loadu_si128(
 6889|  25.5k|        (const __m128i*)(user_input.data() + user_input.length() - 16));
 6890|  25.5k|    running = _mm_or_si128(
 6891|  25.5k|        _mm_or_si128(running, _mm_or_si128(_mm_cmpeq_epi8(word, mask1),
 6892|  25.5k|                                           _mm_cmpeq_epi8(word, mask2))),
 6893|  25.5k|        _mm_cmpeq_epi8(word, mask3));
 6894|  25.5k|  }
 6895|  28.7k|  return _mm_movemask_epi8(running) != 0;
 6896|   121k|}
_ZN3ada7unicode18is_tabs_or_newlineEc:
 6753|   333k|constexpr bool is_tabs_or_newline(char c) noexcept {
 6754|   333k|  return c == '\r' || c == '\n' || c == '\t';
  ------------------
  |  Branch (6754:10): [True: 168, False: 333k]
  |  Branch (6754:23): [True: 150, False: 333k]
  |  Branch (6754:36): [True: 115, False: 333k]
  ------------------
 6755|   333k|}
can_parse.cc:_ZN3ada12_GLOBAL__N_124try_can_parse_clean_httpENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7953|  30.9k|std::optional<bool> try_can_parse_clean_http(std::string_view input) noexcept {
 7954|  30.9k|  const auto* bytes = reinterpret_cast<const uint8_t*>(input.data());
 7955|  30.9k|  const size_t length = input.size();
 7956|  30.9k|  if (length < 7) {
  ------------------
  |  Branch (7956:7): [True: 14.6k, False: 16.3k]
  ------------------
 7957|  14.6k|    return std::nullopt;
 7958|  14.6k|  }
 7959|       |
 7960|  16.3k|  size_t authority_start;
 7961|  16.3k|  if constexpr (k_can_parse_little_endian) {
 7962|  16.3k|    if (length >= 8) {
  ------------------
  |  Branch (7962:9): [True: 14.9k, False: 1.37k]
  ------------------
 7963|  14.9k|      uint64_t first8 = 0;
 7964|  14.9k|      std::memcpy(&first8, bytes, 8);
 7965|  14.9k|      if (first8 == 0x2f2f3a7370747468ull) {  // "https://"
  ------------------
  |  Branch (7965:11): [True: 11, False: 14.9k]
  ------------------
 7966|     11|        authority_start = 8;
 7967|  14.9k|      } else if ((first8 & 0x00ffffffffffffffull) ==
  ------------------
  |  Branch (7967:18): [True: 373, False: 14.6k]
  ------------------
 7968|  14.9k|                 0x002f2f3a70747468ull) {  // "http://"
 7969|    373|        authority_start = 7;
 7970|  14.6k|      } else {
 7971|  14.6k|        return std::nullopt;
 7972|  14.6k|      }
 7973|  14.9k|    } else if (bytes[0] == 'h' && bytes[1] == 't' && bytes[2] == 't' &&
  ------------------
  |  Branch (7973:16): [True: 84, False: 1.28k]
  |  Branch (7973:35): [True: 66, False: 18]
  |  Branch (7973:54): [True: 51, False: 15]
  ------------------
 7974|     51|               bytes[3] == 'p' && bytes[4] == ':' && bytes[5] == '/' &&
  ------------------
  |  Branch (7974:16): [True: 45, False: 6]
  |  Branch (7974:35): [True: 31, False: 14]
  |  Branch (7974:54): [True: 14, False: 17]
  ------------------
 7975|     14|               bytes[6] == '/') {
  ------------------
  |  Branch (7975:16): [True: 2, False: 12]
  ------------------
 7976|      2|      authority_start = 7;
 7977|  1.37k|    } else {
 7978|  1.37k|      return std::nullopt;
 7979|  1.37k|    }
 7980|       |  } else {
 7981|       |    uint32_t first4{};
 7982|       |    std::memcpy(&first4, bytes, 4);
 7983|       |    if (first4 != 0x68747470u) {  // "http"
 7984|       |      return std::nullopt;
 7985|       |    }
 7986|       |    if (length >= 8 && bytes[4] == 's' && bytes[5] == ':' && bytes[6] == '/' &&
 7987|       |        bytes[7] == '/') {
 7988|       |      authority_start = 8;
 7989|       |    } else if (bytes[4] == ':' && bytes[5] == '/' && bytes[6] == '/') {
 7990|       |      authority_start = 7;
 7991|       |    } else {
 7992|       |      return std::nullopt;
 7993|       |    }
 7994|       |  }
 7995|       |
 7996|  16.3k|  if (authority_start >= length) {
  ------------------
  |  Branch (7996:7): [True: 2, False: 16.3k]
  ------------------
 7997|      2|    return false;
 7998|      2|  }
 7999|       |
 8000|  16.3k|  size_t cursor = authority_start;
 8001|  18.0k|  while (cursor + 8 <= length && eight_clean_http_host_bytes(bytes + cursor)) {
  ------------------
  |  Branch (8001:10): [True: 1.84k, False: 16.1k]
  |  Branch (8001:34): [True: 1.67k, False: 178]
  ------------------
 8002|  1.67k|    cursor += 8;
 8003|  1.67k|  }
 8004|  17.0k|  while (cursor < length) {
  ------------------
  |  Branch (8004:10): [True: 994, False: 16.0k]
  ------------------
 8005|    994|    const uint8_t c = bytes[cursor];
 8006|    994|    if (c == '/' || c == '?' || c == '#') {
  ------------------
  |  Branch (8006:9): [True: 233, False: 761]
  |  Branch (8006:21): [True: 1, False: 760]
  |  Branch (8006:33): [True: 1, False: 759]
  ------------------
 8007|    235|      break;
 8008|    235|    }
 8009|    759|    if (!clean_http_host_byte[c]) {
  ------------------
  |  Branch (8009:9): [True: 117, False: 642]
  ------------------
 8010|    117|      return std::nullopt;
 8011|    117|    }
 8012|    642|    ++cursor;
 8013|    642|  }
 8014|       |
 8015|  16.2k|  const size_t host_length = cursor - authority_start;
 8016|  16.2k|  if (host_length == 0) {
  ------------------
  |  Branch (8016:7): [True: 3, False: 16.2k]
  ------------------
 8017|       |    // Extra special-scheme slashes are normalization, not an empty host.
 8018|      3|    if (bytes[authority_start] == '/' || bytes[authority_start] == '\\') {
  ------------------
  |  Branch (8018:9): [True: 1, False: 2]
  |  Branch (8018:42): [True: 0, False: 2]
  ------------------
 8019|      1|      return std::nullopt;
 8020|      1|    }
 8021|      2|    return false;
 8022|      3|  }
 8023|  16.2k|  if (host_length > 253 || bytes[cursor - 1] == '.') {
  ------------------
  |  Branch (8023:7): [True: 15.9k, False: 250]
  |  Branch (8023:28): [True: 31, False: 219]
  ------------------
 8024|     45|    return std::nullopt;
 8025|     45|  }
 8026|       |
 8027|  16.2k|  const std::string_view host(input.data() + authority_start, host_length);
 8028|  16.2k|  if (checkers::last_label_may_be_a_number(host)) {
  ------------------
  |  Branch (8028:7): [True: 18, False: 16.1k]
  ------------------
 8029|     18|    return std::nullopt;
 8030|     18|  }
 8031|  16.1k|  if (host.find('x') != std::string_view::npos &&
  ------------------
  |  Branch (8031:7): [True: 93, False: 16.0k]
  ------------------
 8032|     93|      host.find("xn-") != std::string_view::npos) {
  ------------------
  |  Branch (8032:7): [True: 27, False: 66]
  ------------------
 8033|     27|    return std::nullopt;
 8034|     27|  }
 8035|       |
 8036|       |  // Once a special URL has a valid host, path/query/fragment bytes cannot make
 8037|       |  // it structurally invalid: the full parser percent-encodes them as needed.
 8038|  16.1k|  return true;
 8039|  16.1k|}
can_parse.cc:_ZN3ada12_GLOBAL__N_127eight_clean_http_host_bytesEPKh:
 7929|  1.84k|    const uint8_t* input) noexcept {
 7930|  1.84k|  uint64_t w = 0;
 7931|  1.84k|  std::memcpy(&w, input, 8);
 7932|  1.84k|  constexpr uint64_t k_high = 0x8080808080808080ull;
 7933|  1.84k|  if ((w & k_high) != 0) {
  ------------------
  |  Branch (7933:7): [True: 15, False: 1.83k]
  ------------------
 7934|     15|    return false;
 7935|     15|  }
 7936|  1.83k|  const uint64_t ok = swar_in_range(w, 'a', 'z') | swar_in_range(w, '0', '9') |
 7937|  1.83k|                      swar_in_range(w, '-', '-') | swar_in_range(w, '.', '.') |
 7938|  1.83k|                      swar_in_range(w, '_', '_') | swar_in_range(w, '~', '~');
 7939|  1.83k|  return ok == k_high;
 7940|  1.84k|}
can_parse.cc:_ZN3ada12_GLOBAL__N_113swar_in_rangeEmhh:
 7920|  10.9k|                                         uint8_t hi) noexcept {
 7921|  10.9k|  constexpr uint64_t k_ones = 0x0101010101010101ull;
 7922|  10.9k|  constexpr uint64_t k_high = 0x8080808080808080ull;
 7923|  10.9k|  const uint64_t ge_lo = (w | k_high) - (k_ones * lo);
 7924|  10.9k|  const uint64_t le_hi = ((k_ones * hi) | k_high) - w;
 7925|  10.9k|  return ge_lo & le_hi & k_high;
 7926|  10.9k|}
can_parse.cc:_ZN3ada12_GLOBAL__N_127try_can_parse_absolute_fastENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8054|  30.8k|    std::string_view input) noexcept {
 8055|  30.8k|  const uint8_t* b = reinterpret_cast<const uint8_t*>(input.data());
 8056|  30.8k|  size_t len = input.size();
 8057|       |
 8058|       |  // -- Inline C0 whitespace trim (no allocation) --------------------------
 8059|       |  // Note: \t (0x09), \n (0x0a), \r (0x0d) are all <= 0x20, so any
 8060|       |  // leading/trailing tabs or newlines are correctly stripped here, matching
 8061|       |  // the WHATWG spec's "remove leading/trailing C0 control and space" step.
 8062|  32.2k|  while (len > 0 && b[0] <= 0x20) {
  ------------------
  |  Branch (8062:10): [True: 20.2k, False: 11.9k]
  |  Branch (8062:21): [True: 1.39k, False: 18.8k]
  ------------------
 8063|  1.39k|    b++;
 8064|  1.39k|    len--;
 8065|  1.39k|  }
 8066|  31.6k|  while (len > 0 && b[len - 1] <= 0x20) {
  ------------------
  |  Branch (8066:10): [True: 19.7k, False: 11.9k]
  |  Branch (8066:21): [True: 852, False: 18.8k]
  ------------------
 8067|    852|    len--;
 8068|    852|  }
 8069|  30.8k|  if (len == 0) return false;
  ------------------
  |  Branch (8069:7): [True: 11.9k, False: 18.8k]
  ------------------
 8070|       |
 8071|       |  // -- Scheme detection -----------------------------------------------------
 8072|       |  // Fast path for HTTP and HTTPS (covers ~90%+ of real-world URLs).
 8073|       |  // Avoids the general scheme loop, buffer copy, and perfect hash lookup.
 8074|       |  // We know HTTP and HTTPS are special non-file schemes, so no further
 8075|       |  // scheme_type checks are needed on the fast path -- only `pos` matters.
 8076|  18.8k|  size_t pos;
 8077|       |
 8078|  18.8k|  if (len >= 7 && (b[0] | 0x20) == 'h' && (b[1] | 0x20) == 't' &&
  ------------------
  |  Branch (8078:7): [True: 16.1k, False: 2.76k]
  |  Branch (8078:19): [True: 485, False: 15.6k]
  |  Branch (8078:43): [True: 379, False: 106]
  ------------------
 8079|    379|      (b[2] | 0x20) == 't' && (b[3] | 0x20) == 'p') {
  ------------------
  |  Branch (8079:7): [True: 357, False: 22]
  |  Branch (8079:31): [True: 346, False: 11]
  ------------------
 8080|    346|    if (b[4] == ':' && b[5] == '/' && b[6] == '/') {
  ------------------
  |  Branch (8080:9): [True: 313, False: 33]
  |  Branch (8080:24): [True: 214, False: 99]
  |  Branch (8080:39): [True: 201, False: 13]
  ------------------
 8081|    201|      pos = 7;
 8082|    201|      goto skip_extra_slashes;
 8083|    201|    }
 8084|    145|    if (len >= 8 && (b[4] | 0x20) == 's' && b[5] == ':' && b[6] == '/' &&
  ------------------
  |  Branch (8084:9): [True: 105, False: 40]
  |  Branch (8084:21): [True: 14, False: 91]
  |  Branch (8084:45): [True: 11, False: 3]
  |  Branch (8084:60): [True: 9, False: 2]
  ------------------
 8085|      9|        b[7] == '/') {
  ------------------
  |  Branch (8085:9): [True: 8, False: 1]
  ------------------
 8086|      8|      pos = 8;
 8087|      8|      goto skip_extra_slashes;
 8088|      8|    }
 8089|       |    // Fall through: could be "httpe://", tabs in scheme, etc.
 8090|    145|  }
 8091|       |
 8092|  18.6k|  {
 8093|       |    // General scheme detection for ws, wss, ftp, and edge cases.
 8094|  18.6k|    if (!checkers::is_alpha(static_cast<char>(b[0]))) return false;
  ------------------
  |  Branch (8094:9): [True: 664, False: 18.0k]
  ------------------
 8095|       |
 8096|       |    // Scan for ':' within the first 7 bytes. All special schemes are <= 5
 8097|       |    // chars ("https"), so any URL whose first ':' is beyond byte 6 is either
 8098|       |    // non-special or relative -- both require the full parser.
 8099|  18.0k|    size_t colon_pos = 0;
 8100|  38.7k|    for (size_t i = 1;; ++i) {
 8101|  38.7k|      if (i >= 7 || i >= len) return std::nullopt;
  ------------------
  |  Branch (8101:11): [True: 82, False: 38.7k]
  |  Branch (8101:21): [True: 194, False: 38.5k]
  ------------------
 8102|  38.5k|      const char c = static_cast<char>(b[i]);
 8103|  38.5k|      if (c == ':') {
  ------------------
  |  Branch (8103:11): [True: 17.5k, False: 20.9k]
  ------------------
 8104|  17.5k|        colon_pos = i;
 8105|  17.5k|        break;
 8106|  17.5k|      }
 8107|       |      // Tabs/newlines in the scheme require the full parser to strip them.
 8108|  20.9k|      if (c == '\t' || c == '\n' || c == '\r') return std::nullopt;
  ------------------
  |  Branch (8108:11): [True: 8, False: 20.9k]
  |  Branch (8108:24): [True: 7, False: 20.9k]
  |  Branch (8108:37): [True: 6, False: 20.9k]
  ------------------
 8109|  20.9k|      if (!unicode::is_alnum_plus(c)) return false;
  ------------------
  |  Branch (8109:11): [True: 141, False: 20.7k]
  ------------------
 8110|  20.9k|    }
 8111|       |
 8112|       |    // Lowercase scheme bytes inline and classify via the existing perfect
 8113|       |    // hash.
 8114|  17.5k|    char scheme_buf[6];
 8115|  17.5k|    scheme_buf[0] = static_cast<char>(b[0] | 0x20);
 8116|  37.7k|    for (size_t i = 1; i < colon_pos; ++i)
  ------------------
  |  Branch (8116:24): [True: 20.1k, False: 17.5k]
  ------------------
 8117|  20.1k|      scheme_buf[i] = static_cast<char>(b[i] | 0x20);
 8118|       |
 8119|  17.5k|    const ada::scheme::type scheme_type =
 8120|  17.5k|        ada::scheme::get_scheme_type({scheme_buf, colon_pos});
 8121|       |
 8122|       |    // Only handle special, non-file schemes.
 8123|  17.5k|    if (scheme_type == ada::scheme::NOT_SPECIAL) return std::nullopt;
  ------------------
  |  Branch (8123:9): [True: 2.70k, False: 14.8k]
  ------------------
 8124|  14.8k|    if (scheme_type == ada::scheme::FILE) return std::nullopt;
  ------------------
  |  Branch (8124:9): [True: 1.88k, False: 12.9k]
  ------------------
 8125|       |
 8126|       |    // Per WHATWG, special URLs don't require "//": "http:example.com" is valid
 8127|       |    // (SPECIAL_AUTHORITY_IGNORE_SLASHES just skips leading slashes and
 8128|       |    // proceeds to AUTHORITY).  Defer to the inline fallback for any input
 8129|       |    // without "://".
 8130|  12.9k|    pos = colon_pos + 1;
 8131|  12.9k|    if (pos + 2 > len || b[pos] != '/' || b[pos + 1] != '/') {
  ------------------
  |  Branch (8131:9): [True: 145, False: 12.8k]
  |  Branch (8131:26): [True: 6.08k, False: 6.75k]
  |  Branch (8131:43): [True: 54, False: 6.70k]
  ------------------
 8132|  6.28k|      return std::nullopt;
 8133|  6.28k|    }
 8134|  6.70k|    pos += 2;
 8135|  6.70k|  }
 8136|       |
 8137|  6.91k|skip_extra_slashes:
 8138|       |  // SPECIAL_AUTHORITY_IGNORE_SLASHES: the full parser skips any additional
 8139|       |  // leading '/' or '\' after the initial "//".  Mirror that here so we don't
 8140|       |  // mis-identify the host as empty when there are extra slashes.
 8141|  7.45k|  while (pos < len && (b[pos] == '/' || b[pos] == '\\')) {
  ------------------
  |  Branch (8141:10): [True: 7.43k, False: 25]
  |  Branch (8141:24): [True: 364, False: 7.06k]
  |  Branch (8141:41): [True: 181, False: 6.88k]
  ------------------
 8142|    545|    ++pos;
 8143|    545|  }
 8144|       |
 8145|       |  // Early IPv6 bail-out: if the authority starts with '[', it's an IPv6
 8146|       |  // literal which requires the full parser.  Checking here avoids scanning
 8147|       |  // the entire bracketed address only to bail out afterward.
 8148|  6.91k|  if (pos < len && b[pos] == '[') return std::nullopt;
  ------------------
  |  Branch (8148:7): [True: 6.88k, False: 25]
  |  Branch (8148:20): [True: 95, False: 6.79k]
  ------------------
 8149|       |
 8150|       |  // -- Merged authority + host scan ------------------------------------------
 8151|       |  // A single forward pass over the authority bytes that simultaneously:
 8152|       |  //   - finds the authority end and port colon
 8153|       |  //   - validates host characters (forbidden domain code points)
 8154|       |  //   - tracks IPv4 indicators (all-decimal-dots, last non-dot char)
 8155|       |  //   - detects xn-- prefixes (IDNA punycode)
 8156|       |  //   - detects tabs/newlines (which require the full parser to strip)
 8157|       |  // This replaces 4 separate scans over the host bytes.
 8158|  6.81k|  const size_t auth_start = pos;
 8159|  6.81k|  size_t auth_end = pos;
 8160|  6.81k|  size_t port_colon = SIZE_MAX;
 8161|  6.81k|  bool all_dec_dots = true;
 8162|       |
 8163|  45.1k|  for (; auth_end < len; ++auth_end) {
  ------------------
  |  Branch (8163:10): [True: 44.8k, False: 272]
  ------------------
 8164|  44.8k|    const uint8_t c = b[auth_end];
 8165|       |
 8166|       |    // Non-ASCII -> needs IDNA processing -> full parser.
 8167|  44.8k|    if (c >= 0x80) return std::nullopt;
  ------------------
  |  Branch (8167:9): [True: 31, False: 44.7k]
  ------------------
 8168|       |
 8169|       |    // Authority delimiters.
 8170|  44.7k|    if (c == '/' || c == '?' || c == '#' || c == '\\') break;
  ------------------
  |  Branch (8170:9): [True: 4.73k, False: 40.0k]
  |  Branch (8170:21): [True: 63, False: 40.0k]
  |  Branch (8170:33): [True: 79, False: 39.9k]
  |  Branch (8170:45): [True: 2, False: 39.9k]
  ------------------
 8171|       |
 8172|       |    // Port separator.
 8173|  39.9k|    if (c == ':') {
  ------------------
  |  Branch (8173:9): [True: 1.52k, False: 38.3k]
  ------------------
 8174|  1.52k|      if (port_colon == SIZE_MAX) port_colon = auth_end;
  ------------------
  |  Branch (8174:11): [True: 1.32k, False: 198]
  ------------------
 8175|  1.52k|      continue;
 8176|  1.52k|    }
 8177|       |
 8178|       |    // Credentials or percent-encoding -> full parser.
 8179|  38.3k|    if (c == '@' || c == '%') return std::nullopt;
  ------------------
  |  Branch (8179:9): [True: 109, False: 38.2k]
  |  Branch (8179:21): [True: 184, False: 38.1k]
  ------------------
 8180|       |
 8181|       |    // Tabs/newlines anywhere in the authority require the full parser to
 8182|       |    // strip them before validation.  Without this, a tab in the port (e.g.
 8183|       |    // "http://host:8\t0/") would be mis-rejected by port validation.
 8184|  38.1k|    if (c == '\t' || c == '\n' || c == '\r') return std::nullopt;
  ------------------
  |  Branch (8184:9): [True: 1, False: 38.1k]
  |  Branch (8184:22): [True: 1, False: 38.1k]
  |  Branch (8184:35): [True: 1, False: 38.1k]
  ------------------
 8185|       |
 8186|       |    // Skip remaining host-specific checks for port bytes.  Port digits are
 8187|       |    // validated separately below, and no forbidden-domain-code-point check
 8188|       |    // is needed on port characters.
 8189|  38.1k|    if (port_colon != SIZE_MAX) continue;
  ------------------
  |  Branch (8189:9): [True: 2.22k, False: 35.8k]
  ------------------
 8190|       |
 8191|       |    // -- Host byte validation (inlined) ------------------------------------
 8192|       |    // Forbidden domain code points that are not already caught above:
 8193|       |    //   C0 controls and space (0x00-0x20), DEL (0x7F), <, >, [, ], ^, |.
 8194|       |    // At this stage, the input may still be userinfo or be normalized later
 8195|       |    // (e.g., percent-encoded), so we do not reject here and defer to the
 8196|       |    // parser. Characters already caught: >= 0x80 (non-ASCII), '/' '?' '#' '\\'
 8197|       |    // (delimiters), ':' (port), '@' '%' (bail), '\t' '\n' '\r' (bail).
 8198|  35.8k|    if (c <= 0x20 || c == 0x7F || c == '<' || c == '>' || c == '[' ||
  ------------------
  |  Branch (8198:9): [True: 5, False: 35.8k]
  |  Branch (8198:22): [True: 1, False: 35.8k]
  |  Branch (8198:35): [True: 1, False: 35.8k]
  |  Branch (8198:47): [True: 1, False: 35.8k]
  |  Branch (8198:59): [True: 2, False: 35.8k]
  ------------------
 8199|  35.8k|        c == ']' || c == '^' || c == '|') {
  ------------------
  |  Branch (8199:9): [True: 1, False: 35.8k]
  |  Branch (8199:21): [True: 1, False: 35.8k]
  |  Branch (8199:33): [True: 1, False: 35.8k]
  ------------------
 8200|     13|      return std::nullopt;
 8201|     13|    }
 8202|       |
 8203|       |    // Track whether host is all decimal digits and dots (potential IPv4).
 8204|  35.8k|    if (c != '.' && (c < '0' || c > '9')) all_dec_dots = false;
  ------------------
  |  Branch (8204:9): [True: 31.7k, False: 4.11k]
  |  Branch (8204:22): [True: 2.17k, False: 29.5k]
  |  Branch (8204:33): [True: 21.5k, False: 8.03k]
  ------------------
 8205|       |
 8206|       |    // Detect xn-- prefix inline (IDNA punycode -> needs full parser).
 8207|       |    // Checking at every position mirrors the original behavior: any
 8208|       |    // occurrence of "xn--" in the host (not just at label boundaries)
 8209|       |    // triggers a bail-out to the full IDNA validator.
 8210|  35.8k|    if ((c | 0x20) == 'x' && auth_end + 4 <= len &&
  ------------------
  |  Branch (8210:9): [True: 5.81k, False: 30.0k]
  |  Branch (8210:30): [True: 5.39k, False: 425]
  ------------------
 8211|  5.39k|        (b[auth_end + 1] | 0x20) == 'n' && b[auth_end + 2] == '-' &&
  ------------------
  |  Branch (8211:9): [True: 1.86k, False: 3.53k]
  |  Branch (8211:44): [True: 1.62k, False: 235]
  ------------------
 8212|  1.62k|        b[auth_end + 3] == '-') {
  ------------------
  |  Branch (8212:9): [True: 1.32k, False: 300]
  ------------------
 8213|  1.32k|      return std::nullopt;
 8214|  1.32k|    }
 8215|  35.8k|  }
 8216|       |
 8217|  5.15k|  const size_t host_end = (port_colon != SIZE_MAX) ? port_colon : auth_end;
  ------------------
  |  Branch (8217:27): [True: 1.25k, False: 3.89k]
  ------------------
 8218|       |
 8219|       |  // Empty host is invalid for special URLs.
 8220|  5.15k|  if (auth_start == host_end) return false;
  ------------------
  |  Branch (8220:7): [True: 36, False: 5.11k]
  ------------------
 8221|       |
 8222|       |  // -- IPv4 handling ---------------------------------------------------------
 8223|  5.11k|  const char* host_ptr = reinterpret_cast<const char*>(b + auth_start);
 8224|  5.11k|  const size_t host_len = host_end - auth_start;
 8225|       |
 8226|  5.11k|  if (all_dec_dots) {
  ------------------
  |  Branch (8226:7): [True: 1.31k, False: 3.79k]
  ------------------
 8227|       |    // Host is all decimal digits and dots -> try the fast IPv4 parser.
 8228|  1.31k|    if (checkers::try_parse_ipv4_fast({host_ptr, host_len}) !=
  ------------------
  |  Branch (8228:9): [True: 686, False: 629]
  ------------------
 8229|  1.31k|        checkers::ipv4_fast_fail) {
 8230|       |      // Valid decimal IPv4 host.  Do NOT return true yet: the port still
 8231|       |      // needs to be validated below before we can declare the URL valid.
 8232|    686|      goto validate_port;
 8233|    686|    }
 8234|       |    // Fast IPv4 parsing failed (e.g. host is ".", "..", "1.2.3.500").
 8235|       |    // Such hosts may still be valid domain names; defer to the full parser.
 8236|    629|    return std::nullopt;
 8237|  1.31k|  }
 8238|       |
 8239|  3.79k|  if (checkers::last_label_may_be_a_number({host_ptr, host_len})) {
  ------------------
  |  Branch (8239:7): [True: 271, False: 3.52k]
  ------------------
 8240|    271|    return std::nullopt;
 8241|    271|  }
 8242|       |
 8243|       |  // -- Port validation -------------------------------------------------------
 8244|  4.21k|validate_port:
 8245|  4.21k|  if (port_colon != SIZE_MAX) {
  ------------------
  |  Branch (8245:7): [True: 1.06k, False: 3.15k]
  ------------------
 8246|  1.06k|    const uint8_t* pp = b + port_colon + 1;
 8247|  1.06k|    size_t pl = auth_end - port_colon - 1;
 8248|  1.06k|    if (pl > 0) {
  ------------------
  |  Branch (8248:9): [True: 668, False: 394]
  ------------------
 8249|       |      // Strip leading zeros: "0000001" == 1, "0000000000000" == 0, both valid.
 8250|       |      // Only the significant digits count toward the 5-digit maximum.
 8251|    987|      while (pl > 0 && *pp == '0') {
  ------------------
  |  Branch (8251:14): [True: 847, False: 140]
  |  Branch (8251:24): [True: 319, False: 528]
  ------------------
 8252|    319|        ++pp;
 8253|    319|        --pl;
 8254|    319|      }
 8255|    668|      if (pl > 5) return false;  // significant digits > 99999
  ------------------
  |  Branch (8255:11): [True: 5, False: 663]
  ------------------
 8256|    663|      uint32_t pv = 0;
 8257|  1.51k|      for (size_t i = 0; i < pl; ++i) {
  ------------------
  |  Branch (8257:26): [True: 860, False: 652]
  ------------------
 8258|    860|        if (pp[i] < '0' || pp[i] > '9') return false;
  ------------------
  |  Branch (8258:13): [True: 3, False: 857]
  |  Branch (8258:28): [True: 8, False: 849]
  ------------------
 8259|    849|        pv = pv * 10 + (pp[i] - '0');
 8260|    849|      }
 8261|    652|      if (pv > 65535) return false;
  ------------------
  |  Branch (8261:11): [True: 2, False: 650]
  ------------------
 8262|    652|    }
 8263|  1.06k|  }
 8264|       |
 8265|       |  // Path, query, and fragment are structurally always valid for can_parse --
 8266|       |  // the parser would encode whatever is there.
 8267|  4.19k|  return true;
 8268|  4.21k|}
_ZN3ada7helpers27remove_ascii_tab_or_newlineERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE:
 8564|    788|ada_really_inline void remove_ascii_tab_or_newline(std::string& input) {
 8565|       |  // if this ever becomes a performance issue, we could use an approach similar
 8566|       |  // to has_tabs_or_newline
 8567|    788|  std::erase_if(input, ada::unicode::is_ascii_tab_or_newline);
 8568|    788|}
_ZN3ada7unicode23is_ascii_tab_or_newlineEc:
 7113|  41.4k|    const char c) noexcept {
 7114|  41.4k|  return c == '\t' || c == '\n' || c == '\r';
  ------------------
  |  Branch (7114:10): [True: 1.60k, False: 39.8k]
  |  Branch (7114:23): [True: 2.10k, False: 37.7k]
  |  Branch (7114:36): [True: 775, False: 36.9k]
  ------------------
 7115|  41.4k|}
_ZN3ada7unicode22is_c0_control_or_spaceEc:
 7108|   141k|ada_really_inline constexpr bool is_c0_control_or_space(const char c) noexcept {
 7109|   141k|  return (unsigned char)c <= ' ';
 7110|   141k|}
_ZN3ada7helpers19parse_prepared_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEENS_6scheme4typeERNS1_12basic_stringIcS4_NS1_9allocatorIcEEEE:
 9266|  9.59k|                                           std::string& path) {
 9267|  9.59k|  ada_log("parse_prepared_path ", input);
 9268|  9.59k|  uint8_t accumulator = checkers::path_signature(input);
 9269|       |  // Let us first detect a trivial case.
 9270|       |  // If it is special, we check that we have no dot, no %,  no \ and no
 9271|       |  // character needing percent encoding. Otherwise, we check that we have no %,
 9272|       |  // no dot, and no character needing percent encoding.
 9273|  9.59k|  constexpr uint8_t need_encoding = 1;
 9274|  9.59k|  constexpr uint8_t backslash_char = 2;
 9275|  9.59k|  constexpr uint8_t dot_char = 4;
 9276|  9.59k|  constexpr uint8_t percent_char = 8;
 9277|  9.59k|  bool special = type != ada::scheme::NOT_SPECIAL;
 9278|  9.59k|  bool may_need_slow_file_handling = (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (9278:39): [True: 1.61k, False: 7.98k]
  ------------------
 9279|  1.61k|                                      checkers::is_windows_drive_letter(input));
  ------------------
  |  Branch (9279:39): [True: 135, False: 1.47k]
  ------------------
 9280|  9.59k|  bool trivial_path =
 9281|  9.59k|      (special ? (accumulator == 0)
  ------------------
  |  Branch (9281:7): [True: 4.59k, False: 5.00k]
  |  Branch (9281:8): [True: 8.07k, False: 1.52k]
  ------------------
 9282|  9.59k|               : ((accumulator & (need_encoding | dot_char | percent_char)) ==
 9283|  1.52k|                  0)) &&
 9284|  4.59k|      (!may_need_slow_file_handling);
  ------------------
  |  Branch (9284:7): [True: 4.49k, False: 99]
  ------------------
 9285|  9.59k|  if (accumulator == dot_char && !may_need_slow_file_handling) {
  ------------------
  |  Branch (9285:7): [True: 1.42k, False: 8.17k]
  |  Branch (9285:34): [True: 1.40k, False: 23]
  ------------------
 9286|       |    // '4' means that we have at least one dot, but nothing that requires
 9287|       |    // percent encoding or decoding. The only part that is not trivial is
 9288|       |    // that we may have single dots and double dots path segments.
 9289|       |    // If we have such segments, then we either have a path that begins
 9290|       |    // with '.' (easy to check), or we have the sequence './'.
 9291|       |    // Note: input cannot be empty, it must at least contain one character ('.')
 9292|       |    // Note: we know that '\' is not present.
 9293|  1.40k|    if (input[0] != '.') {
  ------------------
  |  Branch (9293:9): [True: 710, False: 692]
  ------------------
 9294|    710|      size_t slashdot = 0;
 9295|    710|      bool dot_is_file = true;
 9296|  5.13k|      for (;;) {
 9297|  5.13k|        slashdot = input.find("/.", slashdot);
 9298|  5.13k|        if (slashdot == std::string_view::npos) {  // common case
  ------------------
  |  Branch (9298:13): [True: 710, False: 4.42k]
  ------------------
 9299|    710|          break;
 9300|  4.42k|        } else {  // uncommon
 9301|       |          // only three cases matter: /./, /.. or a final /
 9302|  4.42k|          slashdot += 2;
 9303|  4.42k|          dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' ||
  ------------------
  |  Branch (9303:28): [True: 212, False: 4.20k]
  |  Branch (9303:56): [True: 2.61k, False: 1.59k]
  ------------------
 9304|  1.59k|                           input[slashdot] == '/');
  ------------------
  |  Branch (9304:28): [True: 1.19k, False: 397]
  ------------------
 9305|  4.42k|        }
 9306|  5.13k|      }
 9307|    710|      trivial_path = dot_is_file;
 9308|    710|    }
 9309|  1.40k|  }
 9310|  9.59k|  if (trivial_path) {
  ------------------
  |  Branch (9310:7): [True: 4.64k, False: 4.95k]
  ------------------
 9311|  4.64k|    ada_log("parse_path trivial");
 9312|  4.64k|    path += '/';
 9313|  4.64k|    path += input;
 9314|  4.64k|    return;
 9315|  4.64k|  }
 9316|       |  // We are going to need to look a bit at the path, but let us see if we can
 9317|       |  // ignore percent encoding *and* backslashes *and* percent characters.
 9318|       |  // Except for the trivial case, this is likely to capture 99% of paths out
 9319|       |  // there.
 9320|  4.95k|  bool fast_path =
 9321|  4.95k|      (special &&
  ------------------
  |  Branch (9321:8): [True: 3.83k, False: 1.11k]
  ------------------
 9322|  3.83k|       (accumulator & (need_encoding | backslash_char | percent_char)) == 0) &&
  ------------------
  |  Branch (9322:8): [True: 1.21k, False: 2.62k]
  ------------------
 9323|  1.21k|      (type != ada::scheme::type::FILE);
  ------------------
  |  Branch (9323:7): [True: 1.02k, False: 191]
  ------------------
 9324|  4.95k|  if (fast_path) {
  ------------------
  |  Branch (9324:7): [True: 1.02k, False: 3.93k]
  ------------------
 9325|  1.02k|    ada_log("parse_prepared_path fast");
 9326|       |    // Here we don't need to worry about \ or percent encoding.
 9327|       |    // We also do not have a file protocol. We might have dots, however,
 9328|       |    // but dots must as appear as '.', and they cannot be encoded because
 9329|       |    // the symbol '%' is not present.
 9330|  1.02k|    size_t previous_location = 0;  // We start at 0.
 9331|  13.6k|    do {
 9332|  13.6k|      size_t new_location = input.find('/', previous_location);
 9333|       |      // std::string_view path_view = input;
 9334|       |      //  We process the last segment separately:
 9335|  13.6k|      if (new_location == std::string_view::npos) {
  ------------------
  |  Branch (9335:11): [True: 1.02k, False: 12.5k]
  ------------------
 9336|  1.02k|        std::string_view path_view = input.substr(previous_location);
 9337|  1.02k|        if (path_view == "..") {  // The path ends with ..
  ------------------
  |  Branch (9337:13): [True: 121, False: 902]
  ------------------
 9338|       |          // e.g., if you receive ".." with an empty path, you go to "/".
 9339|    121|          if (path.empty()) {
  ------------------
  |  Branch (9339:15): [True: 48, False: 73]
  ------------------
 9340|     48|            path = '/';
 9341|     48|            return;
 9342|     48|          }
 9343|       |          // Fast case where we have nothing to do:
 9344|     73|          if (path.back() == '/') {
  ------------------
  |  Branch (9344:15): [True: 22, False: 51]
  ------------------
 9345|     22|            return;
 9346|     22|          }
 9347|       |          // If you have the path "/joe/myfriend",
 9348|       |          // then you delete 'myfriend'.
 9349|     51|          path.resize(path.rfind('/') + 1);
 9350|     51|          return;
 9351|     73|        }
 9352|    902|        path += '/';
 9353|    902|        if (path_view != ".") {
  ------------------
  |  Branch (9353:13): [True: 648, False: 254]
  ------------------
 9354|    648|          path.append(path_view);
 9355|    648|        }
 9356|    902|        return;
 9357|  12.5k|      } else {
 9358|       |        // This is a non-final segment.
 9359|  12.5k|        std::string_view path_view =
 9360|  12.5k|            input.substr(previous_location, new_location - previous_location);
 9361|  12.5k|        previous_location = new_location + 1;
 9362|  12.5k|        if (path_view == "..") {
  ------------------
  |  Branch (9362:13): [True: 2.89k, False: 9.70k]
  ------------------
 9363|  2.89k|          size_t last_delimiter = path.rfind('/');
 9364|  2.89k|          if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (9364:15): [True: 1.06k, False: 1.82k]
  ------------------
 9365|  1.06k|            path.erase(last_delimiter);
 9366|  1.06k|          }
 9367|  9.70k|        } else if (path_view != ".") {
  ------------------
  |  Branch (9367:20): [True: 7.75k, False: 1.95k]
  ------------------
 9368|  7.75k|          path += '/';
 9369|  7.75k|          path.append(path_view);
 9370|  7.75k|        }
 9371|  12.5k|      }
 9372|  13.6k|    } while (true);
  ------------------
  |  Branch (9372:14): [True: 12.5k, Folded]
  ------------------
 9373|  3.93k|  } else {
 9374|  3.93k|    ada_log("parse_path slow");
 9375|       |    // we have reached the general case
 9376|  3.93k|    bool needs_percent_encoding = (accumulator & 1);
 9377|  3.93k|    std::string path_buffer_tmp;
 9378|  27.0k|    do {
 9379|  27.0k|      size_t location = (special && (accumulator & 2))
  ------------------
  |  Branch (9379:26): [True: 20.1k, False: 6.95k]
  |  Branch (9379:37): [True: 1.80k, False: 18.3k]
  ------------------
 9380|  27.0k|                            ? input.find_first_of("/\\")
 9381|  27.0k|                            : input.find('/');
 9382|  27.0k|      std::string_view path_view = input;
 9383|  27.0k|      if (location != std::string_view::npos) {
  ------------------
  |  Branch (9383:11): [True: 23.1k, False: 3.93k]
  ------------------
 9384|  23.1k|        path_view.remove_suffix(path_view.size() - location);
 9385|  23.1k|        input.remove_prefix(location + 1);
 9386|  23.1k|      }
 9387|       |      // path_buffer is either path_view or it might point at a percent encoded
 9388|       |      // temporary file.
 9389|  27.0k|      std::string_view path_buffer =
 9390|  27.0k|          (needs_percent_encoding &&
  ------------------
  |  Branch (9390:12): [True: 14.8k, False: 12.2k]
  ------------------
 9391|  14.8k|           ada::unicode::percent_encode<false>(
  ------------------
  |  Branch (9391:12): [True: 5.49k, False: 9.33k]
  ------------------
 9392|  14.8k|               path_view, character_sets::PATH_PERCENT_ENCODE, path_buffer_tmp))
 9393|  27.0k|              ? path_buffer_tmp
 9394|  27.0k|              : path_view;
 9395|  27.0k|      if (unicode::is_double_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (9395:11): [True: 4.27k, False: 22.8k]
  ------------------
 9396|  4.27k|        helpers::shorten_path(path, type);
 9397|  4.27k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (9397:13): [True: 259, False: 4.01k]
  ------------------
 9398|    259|          path += '/';
 9399|    259|        }
 9400|  22.8k|      } else if (unicode::is_single_dot_path_segment(path_buffer) &&
  ------------------
  |  Branch (9400:18): [True: 1.37k, False: 21.4k]
  ------------------
 9401|  1.37k|                 (location == std::string_view::npos)) {
  ------------------
  |  Branch (9401:18): [True: 127, False: 1.25k]
  ------------------
 9402|    127|        path += '/';
 9403|    127|      }
 9404|       |      // Otherwise, if path_buffer is not a single-dot path segment, then:
 9405|  22.6k|      else if (!unicode::is_single_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (9405:16): [True: 21.4k, False: 1.25k]
  ------------------
 9406|       |        // If url's scheme is "file", url's path is empty, and path_buffer is a
 9407|       |        // Windows drive letter, then replace the second code point in
 9408|       |        // path_buffer with U+003A (:).
 9409|  21.4k|        if (type == ada::scheme::type::FILE && path.empty() &&
  ------------------
  |  Branch (9409:13): [True: 2.24k, False: 19.1k]
  |  Branch (9409:48): [True: 1.04k, False: 1.20k]
  ------------------
 9410|  1.04k|            checkers::is_windows_drive_letter(path_buffer)) {
  ------------------
  |  Branch (9410:13): [True: 160, False: 880]
  ------------------
 9411|    160|          path += '/';
 9412|    160|          path += path_buffer[0];
 9413|    160|          path += ':';
 9414|    160|          path_buffer.remove_prefix(2);
 9415|    160|          path.append(path_buffer);
 9416|  21.2k|        } else {
 9417|       |          // Append path_buffer to url's path.
 9418|  21.2k|          path += '/';
 9419|  21.2k|          path.append(path_buffer);
 9420|  21.2k|        }
 9421|  21.4k|      }
 9422|  27.0k|      if (location == std::string_view::npos) {
  ------------------
  |  Branch (9422:11): [True: 3.93k, False: 23.1k]
  ------------------
 9423|  3.93k|        return;
 9424|  3.93k|      }
 9425|  27.0k|    } while (true);
  ------------------
  |  Branch (9425:14): [True: 23.1k, Folded]
  ------------------
 9426|  3.93k|  }
 9427|  4.95k|}
_ZN3ada8checkers14path_signatureENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   75|  19.1k|    std::string_view input) noexcept {
   76|       |  // The path percent-encode set is the query percent-encode set and U+003F (?),
   77|       |  // U+0060 (`), U+007B ({), and U+007D (}). The query percent-encode set is the
   78|       |  // C0 control percent-encode set and U+0020 SPACE, U+0022 ("), U+0023 (#),
   79|       |  // U+003C (<), and U+003E (>). The C0 control percent-encode set are the C0
   80|       |  // controls and all code points greater than U+007E (~).
   81|  19.1k|  size_t i = 0;
   82|  19.1k|  uint8_t accumulator{};
   83|  66.7k|  for (; i + 7 < input.size(); i += 8) {
  ------------------
  |  Branch (83:10): [True: 47.5k, False: 19.1k]
  ------------------
   84|  47.5k|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])] |
   85|  47.5k|                           path_signature_table[uint8_t(input[i + 1])] |
   86|  47.5k|                           path_signature_table[uint8_t(input[i + 2])] |
   87|  47.5k|                           path_signature_table[uint8_t(input[i + 3])] |
   88|  47.5k|                           path_signature_table[uint8_t(input[i + 4])] |
   89|  47.5k|                           path_signature_table[uint8_t(input[i + 5])] |
   90|  47.5k|                           path_signature_table[uint8_t(input[i + 6])] |
   91|  47.5k|                           path_signature_table[uint8_t(input[i + 7])]);
   92|  47.5k|  }
   93|  52.4k|  for (; i < input.size(); i++) {
  ------------------
  |  Branch (93:10): [True: 33.2k, False: 19.1k]
  ------------------
   94|  33.2k|    accumulator |= uint8_t(path_signature_table[uint8_t(input[i])]);
   95|  33.2k|  }
   96|  19.1k|  return accumulator;
   97|  19.1k|}
_ZN3ada7unicode26is_double_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7121|  54.2k|    std::string_view input) noexcept {
 7122|       |  // This will catch most cases:
 7123|       |  // The length must be 2,4 or 6.
 7124|       |  // We divide by two and require
 7125|       |  // that the result be between 1 and 3 inclusively.
 7126|  54.2k|  uint64_t half_length = uint64_t(input.size()) / 2;
 7127|  54.2k|  if (half_length - 1 > 2) {
  ------------------
  |  Branch (7127:7): [True: 26.9k, False: 27.2k]
  ------------------
 7128|  26.9k|    return false;
 7129|  26.9k|  }
 7130|       |  // We have a string of length 2, 4 or 6.
 7131|       |  // We now check the first character:
 7132|  27.2k|  if ((input[0] != '.') && (input[0] != '%')) {
  ------------------
  |  Branch (7132:7): [True: 14.8k, False: 12.4k]
  |  Branch (7132:28): [True: 3.36k, False: 11.4k]
  ------------------
 7133|  3.36k|    return false;
 7134|  3.36k|  }
 7135|       |  // We are unlikely the get beyond this point.
 7136|  23.9k|  int hash_value = (input.size() + (unsigned)(input[0])) & 3;
 7137|  23.9k|  const std::string_view target = table_is_double_dot_path_segment[hash_value];
 7138|  23.9k|  if (target.size() != input.size()) {
  ------------------
  |  Branch (7138:7): [True: 10.1k, False: 13.7k]
  ------------------
 7139|  10.1k|    return false;
 7140|  10.1k|  }
 7141|       |  // We almost never get here.
 7142|       |  // Optimizing the rest is relatively unimportant.
 7143|  13.7k|  auto prefix_equal_unsafe = [](std::string_view a, std::string_view b) {
 7144|  13.7k|    uint16_t A, B;
 7145|  13.7k|    memcpy(&A, a.data(), sizeof(A));
 7146|  13.7k|    memcpy(&B, b.data(), sizeof(B));
 7147|  13.7k|    return A == B;
 7148|  13.7k|  };
 7149|  13.7k|  if (!prefix_equal_unsafe(input, target)) {
  ------------------
  |  Branch (7149:7): [True: 2.18k, False: 11.5k]
  ------------------
 7150|  2.18k|    return false;
 7151|  2.18k|  }
 7152|  16.5k|  for (size_t i = 2; i < input.size(); i++) {
  ------------------
  |  Branch (7152:22): [True: 7.96k, False: 8.55k]
  ------------------
 7153|  7.96k|    char c = input[i];
 7154|  7.96k|    if ((uint8_t((c | 0x20) - 0x61) <= 25 ? (c | 0x20) : c) != target[i]) {
  ------------------
  |  Branch (7154:9): [True: 2.99k, False: 4.96k]
  |  Branch (7154:10): [True: 2.29k, False: 5.66k]
  ------------------
 7155|  2.99k|      return false;
 7156|  2.99k|    }
 7157|  7.96k|  }
 7158|  8.55k|  return true;
 7159|       |  // The above code might be a bit better than the code below. Compilers
 7160|       |  // are not stupid and may use the fact that these strings have length 2,4 and
 7161|       |  // 6 and other tricks.
 7162|       |  // return input == ".." ||
 7163|       |  //  input == ".%2e" || input == ".%2E" ||
 7164|       |  //  input == "%2e." || input == "%2E." ||
 7165|       |  //  input == "%2e%2e" || input == "%2E%2E" || input == "%2E%2e" || input ==
 7166|       |  //  "%2e%2E";
 7167|  11.5k|}
_ZZN3ada7unicode26is_double_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEENKUlS5_S5_E_clES5_S5_:
 7143|  13.7k|  auto prefix_equal_unsafe = [](std::string_view a, std::string_view b) {
 7144|  13.7k|    uint16_t A, B;
 7145|  13.7k|    memcpy(&A, a.data(), sizeof(A));
 7146|  13.7k|    memcpy(&B, b.data(), sizeof(B));
 7147|  13.7k|    return A == B;
 7148|  13.7k|  };
_ZN3ada7unicode26is_single_dot_path_segmentENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7170|  91.0k|    std::string_view input) noexcept {
 7171|  91.0k|  return input == "." || input == "%2e" || input == "%2E";
  ------------------
  |  Branch (7171:10): [True: 3.78k, False: 87.3k]
  |  Branch (7171:26): [True: 1.48k, False: 85.8k]
  |  Branch (7171:44): [True: 0, False: 85.8k]
  ------------------
 7172|  91.0k|}
_ZN3ada7unicode28is_forbidden_host_code_pointEc:
 6991|  70.5k|    const char c) noexcept {
 6992|  70.5k|  return is_forbidden_host_code_point_table[uint8_t(c)];
 6993|  70.5k|}
_ZN3ada6detail17parse_ipv4_numberERPKcS2_RmRb:
 9550|  6.74k|                              bool& is_pure_decimal) noexcept {
 9551|  6.74k|  is_pure_decimal = false;
 9552|  6.74k|  if (p >= end) [[unlikely]] {
  ------------------
  |  Branch (9552:7): [True: 0, False: 6.74k]
  ------------------
 9553|      0|    return false;
 9554|      0|  }
 9555|  6.74k|  if (end - p >= 2 && p[0] == '0' && ((p[1] | 0x20) == 'x')) {
  ------------------
  |  Branch (9555:7): [True: 4.66k, False: 2.07k]
  |  Branch (9555:23): [True: 2.36k, False: 2.30k]
  |  Branch (9555:38): [True: 1.48k, False: 882]
  ------------------
 9556|  1.48k|    p += 2;
 9557|  1.48k|    if (p == end || *p == '.') {
  ------------------
  |  Branch (9557:9): [True: 264, False: 1.21k]
  |  Branch (9557:21): [True: 63, False: 1.15k]
  ------------------
 9558|    327|      value = 0;
 9559|    327|      return true;
 9560|    327|    }
 9561|  1.15k|    uint64_t v = 0;
 9562|  1.15k|    int digits = 0;
 9563|  7.95k|    while (p < end && *p != '.') {
  ------------------
  |  Branch (9563:12): [True: 7.19k, False: 765]
  |  Branch (9563:23): [True: 7.08k, False: 105]
  ------------------
 9564|  7.08k|      const uint8_t nib = hex_nibble[static_cast<unsigned char>(*p)];
 9565|  7.08k|      if (nib == 0xff) [[unlikely]] {
  ------------------
  |  Branch (9565:11): [True: 18, False: 7.06k]
  ------------------
 9566|     18|        return false;
 9567|     18|      }
 9568|  7.06k|      if (v > (0xFFFFFFFFULL >> 4)) [[unlikely]] {
  ------------------
  |  Branch (9568:11): [True: 267, False: 6.80k]
  ------------------
 9569|    267|        return false;
 9570|    267|      }
 9571|  6.80k|      v = (v << 4) | nib;
 9572|  6.80k|      ++p;
 9573|  6.80k|      ++digits;
 9574|  6.80k|    }
 9575|    870|    if (digits == 0) [[unlikely]] {
  ------------------
  |  Branch (9575:9): [True: 0, False: 870]
  ------------------
 9576|      0|      return false;
 9577|      0|    }
 9578|    870|    value = v;
 9579|    870|    return true;
 9580|    870|  }
 9581|  5.25k|  if (end - p >= 2 && p[0] == '0' && p[1] >= '0' && p[1] <= '9') {
  ------------------
  |  Branch (9581:7): [True: 3.18k, False: 2.07k]
  |  Branch (9581:23): [True: 882, False: 2.30k]
  |  Branch (9581:38): [True: 501, False: 381]
  |  Branch (9581:53): [True: 483, False: 18]
  ------------------
 9582|    483|    ++p;
 9583|    483|    uint64_t v = 0;
 9584|  4.04k|    while (p < end && *p != '.') {
  ------------------
  |  Branch (9584:12): [True: 3.75k, False: 291]
  |  Branch (9584:23): [True: 3.63k, False: 117]
  ------------------
 9585|  3.63k|      const char c = *p;
 9586|  3.63k|      if (c < '0' || c > '7') [[unlikely]] {
  ------------------
  |  Branch (9586:11): [True: 15, False: 3.62k]
  |  Branch (9586:22): [True: 45, False: 3.57k]
  ------------------
 9587|     60|        return false;
 9588|     60|      }
 9589|  3.57k|      if (v > (0xFFFFFFFFULL >> 3)) [[unlikely]] {
  ------------------
  |  Branch (9589:11): [True: 15, False: 3.56k]
  ------------------
 9590|     15|        return false;
 9591|     15|      }
 9592|  3.56k|      v = (v << 3) | static_cast<uint64_t>(c - '0');
 9593|  3.56k|      ++p;
 9594|  3.56k|    }
 9595|    408|    value = v;
 9596|    408|    return true;
 9597|    483|  }
 9598|  4.77k|  if (*p < '0' || *p > '9') [[unlikely]] {
  ------------------
  |  Branch (9598:7): [True: 156, False: 4.62k]
  |  Branch (9598:19): [True: 309, False: 4.31k]
  ------------------
 9599|    465|    return false;
 9600|    465|  }
 9601|  4.31k|  is_pure_decimal = true;
 9602|  4.31k|  uint64_t v = static_cast<uint64_t>(*p - '0');
 9603|  4.31k|  ++p;
 9604|  8.88k|  while (p < end && *p != '.') {
  ------------------
  |  Branch (9604:10): [True: 6.07k, False: 2.81k]
  |  Branch (9604:21): [True: 4.78k, False: 1.29k]
  ------------------
 9605|  4.78k|    const char c = *p;
 9606|  4.78k|    if (c < '0' || c > '9') [[unlikely]] {
  ------------------
  |  Branch (9606:9): [True: 27, False: 4.75k]
  |  Branch (9606:20): [True: 81, False: 4.67k]
  ------------------
 9607|    108|      return false;
 9608|    108|    }
 9609|  4.67k|    if (v > 429496729ULL) [[unlikely]] {
  ------------------
  |  Branch (9609:9): [True: 84, False: 4.59k]
  ------------------
 9610|     84|      return false;
 9611|     84|    }
 9612|  4.59k|    v = v * 10u + static_cast<uint64_t>(c - '0');
 9613|  4.59k|    if (v > 0xFFFFFFFFULL) [[unlikely]] {
  ------------------
  |  Branch (9613:9): [True: 12, False: 4.57k]
  ------------------
 9614|     12|      return false;
 9615|     12|    }
 9616|  4.57k|    ++p;
 9617|  4.57k|  }
 9618|  4.10k|  value = v;
 9619|  4.10k|  return true;
 9620|  4.31k|}
_ZN3ada6detail15parse_hex_pieceERPKcS2_Rt:
 9624|  2.63k|                           uint16_t& value) noexcept {
 9625|  2.63k|  if (pointer == end) {
  ------------------
  |  Branch (9625:7): [True: 0, False: 2.63k]
  ------------------
 9626|      0|    return 0;
 9627|      0|  }
 9628|  2.63k|  const uint8_t n0 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9629|  2.63k|  if (n0 == 0xff) {
  ------------------
  |  Branch (9629:7): [True: 168, False: 2.46k]
  ------------------
 9630|    168|    return 0;
 9631|    168|  }
 9632|  2.46k|  uint32_t v = n0;
 9633|  2.46k|  ++pointer;
 9634|  2.46k|  int length = 1;
 9635|  2.46k|  if (pointer != end) {
  ------------------
  |  Branch (9635:7): [True: 2.19k, False: 273]
  ------------------
 9636|  2.19k|    const uint8_t n1 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9637|  2.19k|    if (n1 != 0xff) {
  ------------------
  |  Branch (9637:9): [True: 867, False: 1.32k]
  ------------------
 9638|    867|      v = (v << 4) | n1;
 9639|    867|      ++pointer;
 9640|    867|      ++length;
 9641|    867|      if (pointer != end) {
  ------------------
  |  Branch (9641:11): [True: 774, False: 93]
  ------------------
 9642|    774|        const uint8_t n2 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9643|    774|        if (n2 != 0xff) {
  ------------------
  |  Branch (9643:13): [True: 561, False: 213]
  ------------------
 9644|    561|          v = (v << 4) | n2;
 9645|    561|          ++pointer;
 9646|    561|          ++length;
 9647|    561|          if (pointer != end) {
  ------------------
  |  Branch (9647:15): [True: 471, False: 90]
  ------------------
 9648|    471|            const uint8_t n3 = hex_nibble[static_cast<unsigned char>(*pointer)];
 9649|    471|            if (n3 != 0xff) {
  ------------------
  |  Branch (9649:17): [True: 222, False: 249]
  ------------------
 9650|    222|              v = (v << 4) | n3;
 9651|    222|              ++pointer;
 9652|    222|              ++length;
 9653|    222|            }
 9654|    471|          }
 9655|    561|        }
 9656|    774|      }
 9657|    867|    }
 9658|  2.19k|  }
 9659|  2.46k|  value = static_cast<uint16_t>(v);
 9660|  2.46k|  return length;
 9661|  2.63k|}
_ZN3ada3url10parse_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10233|  1.85k|ada_really_inline void url::parse_path(std::string_view input) {
10234|  1.85k|  ada_log("parse_path ", input);
10235|  1.85k|  std::string tmp_buffer;
10236|  1.85k|  std::string_view internal_input;
10237|  1.85k|  if (unicode::has_tabs_or_newline(input)) {
  ------------------
  |  Branch (10237:7): [True: 0, False: 1.85k]
  ------------------
10238|      0|    tmp_buffer = input;
10239|       |    // Optimization opportunity: Instead of copying and then pruning, we could
10240|       |    // just directly build the string from user_input.
10241|      0|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
10242|      0|    internal_input = tmp_buffer;
10243|  1.85k|  } else {
10244|  1.85k|    internal_input = input;
10245|  1.85k|  }
10246|       |
10247|       |  // If url is special, then:
10248|  1.85k|  if (is_special()) {
  ------------------
  |  Branch (10248:7): [True: 1.85k, False: 0]
  ------------------
10249|  1.85k|    if (internal_input.empty()) {
  ------------------
  |  Branch (10249:9): [True: 73, False: 1.78k]
  ------------------
10250|     73|      path = "/";
10251|  1.78k|    } else if ((internal_input[0] == '/') || (internal_input[0] == '\\')) {
  ------------------
  |  Branch (10251:16): [True: 1.78k, False: 0]
  |  Branch (10251:46): [True: 0, False: 0]
  ------------------
10252|  1.78k|      helpers::parse_prepared_path(internal_input.substr(1), type, path);
10253|  1.78k|    } else {
10254|      0|      helpers::parse_prepared_path(internal_input, type, path);
10255|      0|    }
10256|  1.85k|  } else if (!internal_input.empty()) {
  ------------------
  |  Branch (10256:14): [True: 0, False: 0]
  ------------------
10257|      0|    if (internal_input[0] == '/') {
  ------------------
  |  Branch (10257:9): [True: 0, False: 0]
  ------------------
10258|      0|      helpers::parse_prepared_path(internal_input.substr(1), type, path);
10259|      0|    } else {
10260|      0|      helpers::parse_prepared_path(internal_input, type, path);
10261|      0|    }
10262|      0|  } else {
10263|      0|    if (!host.has_value()) {
  ------------------
  |  Branch (10263:9): [True: 0, False: 0]
  ------------------
10264|      0|      path = "/";
10265|      0|    }
10266|      0|  }
10267|  1.85k|}
_ZN3ada7unicode13is_alnum_plusEc:
 7085|   171k|ada_really_inline constexpr bool is_alnum_plus(const char c) noexcept {
 7086|   171k|  return is_alnum_plus_table[uint8_t(c)];
 7087|       |  // A table is almost surely much faster than the
 7088|       |  // following under most compilers: return
 7089|       |  // return (std::isalnum(c) || c == '+' || c == '-' || c == '.');
 7090|   171k|}
_ZN3ada7unicode14to_lower_asciiEPcm:
 6761|  39.1k|constexpr bool to_lower_ascii(char* input, size_t length) noexcept {
 6762|  39.1k|  uint64_t broadcast_80 = broadcast(0x80);
 6763|  39.1k|  uint64_t broadcast_Ap = broadcast(128 - 'A');
 6764|  39.1k|  uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1);
 6765|  39.1k|  uint64_t non_ascii = 0;
 6766|  39.1k|  size_t i = 0;
 6767|       |
 6768|  48.6k|  for (; i + 7 < length; i += 8) {
  ------------------
  |  Branch (6768:10): [True: 9.45k, False: 39.1k]
  ------------------
 6769|  9.45k|    uint64_t word{};
 6770|  9.45k|    memcpy(&word, input + i, sizeof(word));
 6771|  9.45k|    non_ascii |= (word & broadcast_80);
 6772|  9.45k|    word ^=
 6773|  9.45k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 6774|  9.45k|    memcpy(input + i, &word, sizeof(word));
 6775|  9.45k|  }
 6776|  39.1k|  if (i < length) {
  ------------------
  |  Branch (6776:7): [True: 38.3k, False: 849]
  ------------------
 6777|  38.3k|    uint64_t word{};
 6778|  38.3k|    memcpy(&word, input + i, length - i);
 6779|  38.3k|    non_ascii |= (word & broadcast_80);
 6780|  38.3k|    word ^=
 6781|  38.3k|        (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
 6782|  38.3k|    memcpy(input + i, &word, length - i);
 6783|  38.3k|  }
 6784|  39.1k|  return non_ascii == 0;
 6785|  39.1k|}
_ZN3ada7unicode9broadcastEh:
 6757|   117k|constexpr uint64_t broadcast(uint8_t v) noexcept {
 6758|   117k|  return 0x101010101010101ull * v;
 6759|   117k|}
_ZZN3ada6parser25try_parse_simple_absoluteINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_ENKUlvE_clEv:
12233|  1.05k|    auto apply_query_and_hash = [&]() {
12234|  1.05k|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12234:11): [True: 81, False: 976]
  ------------------
12235|     81|        const size_t q_end =
12236|     81|            (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12236:13): [True: 16, False: 65]
  ------------------
12237|     81|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12238|     81|                                                q_end - query_start - 1),
12239|     81|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12240|     81|      }
12241|  1.05k|      if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12241:11): [True: 86, False: 971]
  ------------------
12242|     86|        out.update_unencoded_base_hash(std::string_view(
12243|     86|            input.data() + hash_start + 1, len - hash_start - 1));
12244|     86|      }
12245|  1.05k|    };
_ZZN3ada6parser25try_parse_simple_absoluteINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_ENKUlvE_clEv:
12233|  1.05k|    auto apply_query_and_hash = [&]() {
12234|  1.05k|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (12234:11): [True: 81, False: 976]
  ------------------
12235|     81|        const size_t q_end =
12236|     81|            (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (12236:13): [True: 16, False: 65]
  ------------------
12237|     81|        out.update_base_search(std::string_view(input.data() + query_start + 1,
12238|     81|                                                q_end - query_start - 1),
12239|     81|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
12240|     81|      }
12241|  1.05k|      if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (12241:11): [True: 86, False: 971]
  ------------------
12242|     86|        out.update_unencoded_base_hash(std::string_view(
12243|     86|            input.data() + hash_start + 1, len - hash_start - 1));
12244|     86|      }
12245|  1.05k|    };
_ZZN3ada6parser32finish_simple_absolute_with_portINS_3urlEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_NS_6scheme4typeEjmmmbENKUlvE_clEv:
11833|    800|    auto apply_query_and_hash = [&]() {
11834|    800|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (11834:11): [True: 56, False: 744]
  ------------------
11835|     56|        const size_t q_end =
11836|     56|            (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (11836:13): [True: 15, False: 41]
  ------------------
11837|     56|        out.update_base_search(std::string_view(input.data() + query_start + 1,
11838|     56|                                                q_end - query_start - 1),
11839|     56|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
11840|     56|      }
11841|    800|      if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (11841:11): [True: 39, False: 761]
  ------------------
11842|     39|        out.update_unencoded_base_hash(std::string_view(
11843|     39|            input.data() + hash_start + 1, len - hash_start - 1));
11844|     39|      }
11845|    800|    };
_ZZN3ada6parser32finish_simple_absolute_with_portINS_14url_aggregatorEEEbNSt3__117basic_string_viewIcNS3_11char_traitsIcEEEERT_NS_6scheme4typeEjmmmbENKUlvE_clEv:
11833|    800|    auto apply_query_and_hash = [&]() {
11834|    800|      if (query_start != std::string_view::npos) {
  ------------------
  |  Branch (11834:11): [True: 56, False: 744]
  ------------------
11835|     56|        const size_t q_end =
11836|     56|            (hash_start != std::string_view::npos) ? hash_start : len;
  ------------------
  |  Branch (11836:13): [True: 15, False: 41]
  ------------------
11837|     56|        out.update_base_search(std::string_view(input.data() + query_start + 1,
11838|     56|                                                q_end - query_start - 1),
11839|     56|                               character_sets::SPECIAL_QUERY_PERCENT_ENCODE);
11840|     56|      }
11841|    800|      if (hash_start != std::string_view::npos) {
  ------------------
  |  Branch (11841:11): [True: 39, False: 761]
  ------------------
11842|     39|        out.update_unencoded_base_hash(std::string_view(
11843|     39|            input.data() + hash_start + 1, len - hash_start - 1));
11844|     39|      }
11845|    800|    };
_ZN3ada7helpers10prune_hashERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8504|   113k|    std::string_view& input) noexcept {
 8505|       |  // compiles down to 20--30 instructions including a class to memchr (C
 8506|       |  // function). this function should be quite fast.
 8507|   113k|  size_t location_of_first = input.find('#');
 8508|   113k|  if (location_of_first == std::string_view::npos) {
  ------------------
  |  Branch (8508:7): [True: 111k, False: 2.70k]
  ------------------
 8509|   111k|    return std::nullopt;
 8510|   111k|  }
 8511|  2.70k|  std::string_view hash = input;
 8512|  2.70k|  hash.remove_prefix(location_of_first + 1);
 8513|  2.70k|  input.remove_suffix(input.size() - location_of_first);
 8514|  2.70k|  return hash;
 8515|   113k|}
_ZZN3ada6parser14parse_url_implINS_3urlELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_ENKUlvE_clEv:
12556|  18.0k|  const auto enforce_max_input_length = [&]() {
12557|  18.0k|    if constexpr (store_values) {
12558|  18.0k|      if (url.is_valid) {
  ------------------
  |  Branch (12558:11): [True: 18.0k, False: 0]
  ------------------
12559|       |        if constexpr (result_type_is_ada_url_aggregator) {
12560|       |          if (url.buffer.size() > max_input_length) [[unlikely]] {
12561|       |            url.is_valid = false;
12562|       |          }
12563|  18.0k|        } else {
12564|  18.0k|          if (url.get_href_size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12564:15): [True: 0, False: 18.0k]
  ------------------
12565|      0|            url.is_valid = false;
12566|      0|          }
12567|  18.0k|        }
12568|  18.0k|      }
12569|  18.0k|    }
12570|  18.0k|  };
_ZN3ada7helpers32find_authority_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9462|  23.1k|find_authority_delimiter_special(std::string_view view) noexcept {
 9463|       |  // performance note: we might be able to gain further performance
 9464|       |  // with SIMD intrinsics.
 9465|   153k|  for (auto pos = view.begin(); pos != view.end(); ++pos) {
  ------------------
  |  Branch (9465:33): [True: 151k, False: 2.08k]
  ------------------
 9466|   151k|    if (authority_delimiter_special[(uint8_t)*pos]) {
  ------------------
  |  Branch (9466:9): [True: 21.0k, False: 130k]
  ------------------
 9467|  21.0k|      return pos - view.begin();
 9468|  21.0k|    }
 9469|   151k|  }
 9470|  2.08k|  return size_t(view.size());
 9471|  23.1k|}
_ZN3ada7helpers24find_authority_delimiterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9483|  2.87k|find_authority_delimiter(std::string_view view) noexcept {
 9484|       |  // performance note: we might be able to gain further performance
 9485|       |  // with SIMD instrinsics.
 9486|  42.7k|  for (auto pos = view.begin(); pos != view.end(); ++pos) {
  ------------------
  |  Branch (9486:33): [True: 42.4k, False: 354]
  ------------------
 9487|  42.4k|    if (authority_delimiter[(uint8_t)*pos]) {
  ------------------
  |  Branch (9487:9): [True: 2.51k, False: 39.9k]
  ------------------
 9488|  2.51k|      return pos - view.begin();
 9489|  2.51k|    }
 9490|  42.4k|  }
 9491|    354|  return size_t(view.size());
 9492|  2.87k|}
_ZN3ada7helpers12shorten_pathERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS_6scheme4typeE:
 8517|  8.99k|ada_really_inline bool shorten_path(std::string& path, ada::scheme::type type) {
 8518|       |  // Let path be url's path.
 8519|       |  // If url's scheme is "file", path's size is 1, and path[0] is a normalized
 8520|       |  // Windows drive letter, then return.
 8521|  8.99k|  if (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (8521:7): [True: 3.47k, False: 5.52k]
  ------------------
 8522|  3.47k|      path.find('/', 1) == std::string_view::npos && !path.empty()) {
  ------------------
  |  Branch (8522:7): [True: 2.55k, False: 926]
  |  Branch (8522:54): [True: 1.76k, False: 788]
  ------------------
 8523|  1.76k|    if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (8523:9): [True: 742, False: 1.02k]
  ------------------
 8524|  1.76k|            helpers::substring(path, 1))) {
 8525|    742|      return false;
 8526|    742|    }
 8527|  1.76k|  }
 8528|       |
 8529|       |  // Remove path's last item, if any.
 8530|  8.25k|  size_t last_delimiter = path.rfind('/');
 8531|  8.25k|  if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (8531:7): [True: 4.30k, False: 3.94k]
  ------------------
 8532|  4.30k|    path.erase(last_delimiter);
 8533|  4.30k|    return true;
 8534|  4.30k|  }
 8535|       |
 8536|  3.94k|  return false;
 8537|  8.25k|}
_ZN3ada7helpers27get_host_delimiter_locationEbRNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9175|  38.8k|    const bool is_special, std::string_view& view) noexcept {
 9176|       |  /**
 9177|       |   * The spec at https://url.spec.whatwg.org/#hostname-state expects us to
 9178|       |   * compute a variable called insideBrackets but this variable is only used
 9179|       |   * once, to check whether a ':' character was found outside brackets. Exact
 9180|       |   * text: "Otherwise, if c is U+003A (:) and insideBrackets is false, then:".
 9181|       |   * It is conceptually simpler and arguably more efficient to just return a
 9182|       |   * Boolean indicating whether ':' was found outside brackets.
 9183|       |   */
 9184|  38.8k|  const size_t view_size = view.size();
 9185|  38.8k|  size_t location = 0;
 9186|  38.8k|  bool found_colon = false;
 9187|       |  /**
 9188|       |   * Performance analysis:
 9189|       |   *
 9190|       |   * We are basically seeking the end of the hostname which can be indicated
 9191|       |   * by the end of the view, or by one of the characters ':', '/', '?', '\\'
 9192|       |   * (where '\\' is only applicable for special URLs). However, these must
 9193|       |   * appear outside a bracket range. E.g., if you have [something?]fd: then the
 9194|       |   * '?' does not count.
 9195|       |   *
 9196|       |   * So we can skip ahead to the next delimiter, as long as we include '[' in
 9197|       |   * the set of delimiters, and that we handle it first.
 9198|       |   *
 9199|       |   * So the trick is to have a fast function that locates the next delimiter.
 9200|       |   * Unless we find '[', then it only needs to be called once! Ideally, such a
 9201|       |   * function would be provided by the C++ standard library, but it seems that
 9202|       |   * find_first_of is not very fast, so we are forced to roll our own.
 9203|       |   *
 9204|       |   * We do not break into two loops for speed, but for clarity.
 9205|       |   */
 9206|  38.8k|  if (is_special) {
  ------------------
  |  Branch (9206:7): [True: 36.2k, False: 2.63k]
  ------------------
 9207|       |    // We move to the next delimiter.
 9208|  36.2k|    location = find_next_host_delimiter_special(view, location);
 9209|       |    // Unless we find '[' then we are going only going to have to call
 9210|       |    // find_next_host_delimiter_special once.
 9211|  39.6k|    for (; location < view_size;
  ------------------
  |  Branch (9211:12): [True: 20.9k, False: 18.6k]
  ------------------
 9212|  36.2k|         location = find_next_host_delimiter_special(view, location)) {
 9213|  20.9k|      if (view[location] == '[') {
  ------------------
  |  Branch (9213:11): [True: 3.57k, False: 17.3k]
  ------------------
 9214|  3.57k|        location = view.find(']', location);
 9215|  3.57k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (9215:13): [True: 210, False: 3.36k]
  ------------------
 9216|       |          // performance: view.find might get translated to a memchr, which
 9217|       |          // has no notion of std::string_view::npos, so the code does not
 9218|       |          // reflect the assembly.
 9219|    210|          location = view_size;
 9220|    210|          break;
 9221|    210|        }
 9222|  17.3k|      } else {
 9223|  17.3k|        found_colon = view[location] == ':';
 9224|  17.3k|        break;
 9225|  17.3k|      }
 9226|  20.9k|    }
 9227|  36.2k|  } else {
 9228|       |    // We move to the next delimiter.
 9229|  2.63k|    location = find_next_host_delimiter(view, location);
 9230|       |    // Unless we find '[' then we are going only going to have to call
 9231|       |    // find_next_host_delimiter_special once.
 9232|  4.29k|    for (; location < view_size;
  ------------------
  |  Branch (9232:12): [True: 2.45k, False: 1.83k]
  ------------------
 9233|  2.63k|         location = find_next_host_delimiter(view, location)) {
 9234|  2.45k|      if (view[location] == '[') {
  ------------------
  |  Branch (9234:11): [True: 1.70k, False: 756]
  ------------------
 9235|  1.70k|        location = view.find(']', location);
 9236|  1.70k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (9236:13): [True: 39, False: 1.66k]
  ------------------
 9237|       |          // performance: view.find might get translated to a memchr, which
 9238|       |          // has no notion of std::string_view::npos, so the code does not
 9239|       |          // reflect the assembly.
 9240|     39|          location = view_size;
 9241|     39|          break;
 9242|     39|        }
 9243|  1.70k|      } else {
 9244|    756|        found_colon = view[location] == ':';
 9245|    756|        break;
 9246|    756|      }
 9247|  2.45k|    }
 9248|  2.63k|  }
 9249|       |  // performance: remove_suffix may translate into a single instruction.
 9250|  38.8k|  view.remove_suffix(view_size - location);
 9251|  38.8k|  return {location, found_colon};
 9252|  38.8k|}
_ZN3ada7helpers32find_next_host_delimiter_specialENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 8765|  39.6k|    std::string_view view, size_t location) noexcept {
 8766|       |  // first check for short strings in which case we do it naively.
 8767|  39.6k|  if (view.size() - location < 16) {  // slow path
  ------------------
  |  Branch (8767:7): [True: 26.5k, False: 13.0k]
  ------------------
 8768|   146k|    for (size_t i = location; i < view.size(); i++) {
  ------------------
  |  Branch (8768:31): [True: 132k, False: 13.4k]
  ------------------
 8769|   132k|      if (view[i] == ':' || view[i] == '/' || view[i] == '\\' ||
  ------------------
  |  Branch (8769:11): [True: 4.72k, False: 128k]
  |  Branch (8769:29): [True: 6.11k, False: 121k]
  |  Branch (8769:47): [True: 32, False: 121k]
  ------------------
 8770|   121k|          view[i] == '?' || view[i] == '[') {
  ------------------
  |  Branch (8770:11): [True: 570, False: 121k]
  |  Branch (8770:29): [True: 1.69k, False: 119k]
  ------------------
 8771|  13.1k|        return i;
 8772|  13.1k|      }
 8773|   132k|    }
 8774|  13.4k|    return size_t(view.size());
 8775|  26.5k|  }
 8776|       |  // fast path for long strings (expected to be common)
 8777|  13.0k|  size_t i = location;
 8778|  13.0k|  const __m128i mask1 = _mm_set1_epi8(':');
 8779|  13.0k|  const __m128i mask2 = _mm_set1_epi8('/');
 8780|  13.0k|  const __m128i mask3 = _mm_set1_epi8('\\');
 8781|  13.0k|  const __m128i mask4 = _mm_set1_epi8('?');
 8782|  13.0k|  const __m128i mask5 = _mm_set1_epi8('[');
 8783|       |
 8784|  54.5k|  for (; i + 15 < view.size(); i += 16) {
  ------------------
  |  Branch (8784:10): [True: 46.3k, False: 8.14k]
  ------------------
 8785|  46.3k|    __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
 8786|  46.3k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 8787|  46.3k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 8788|  46.3k|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
 8789|  46.3k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 8790|  46.3k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 8791|  46.3k|    __m128i m = _mm_or_si128(
 8792|  46.3k|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
 8793|  46.3k|    int mask = _mm_movemask_epi8(m);
 8794|  46.3k|    if (mask != 0) {
  ------------------
  |  Branch (8794:9): [True: 4.92k, False: 41.4k]
  ------------------
 8795|  4.92k|      return i + trailing_zeroes(mask);
 8796|  4.92k|    }
 8797|  46.3k|  }
 8798|  8.14k|  if (i < view.size()) {
  ------------------
  |  Branch (8798:7): [True: 7.52k, False: 626]
  ------------------
 8799|  7.52k|    __m128i word =
 8800|  7.52k|        _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
 8801|  7.52k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 8802|  7.52k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 8803|  7.52k|    __m128i m3 = _mm_cmpeq_epi8(word, mask3);
 8804|  7.52k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 8805|  7.52k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 8806|  7.52k|    __m128i m = _mm_or_si128(
 8807|  7.52k|        _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m3, m4)), m5);
 8808|  7.52k|    int mask = _mm_movemask_epi8(m);
 8809|  7.52k|    if (mask != 0) {
  ------------------
  |  Branch (8809:9): [True: 2.91k, False: 4.60k]
  ------------------
 8810|  2.91k|      return view.length() - 16 + trailing_zeroes(mask);
 8811|  2.91k|    }
 8812|  7.52k|  }
 8813|  5.23k|  return size_t(view.length());
 8814|  8.14k|}
_ZN3ada7helpers15trailing_zeroesEj:
 8585|  9.24k|ada_really_inline int trailing_zeroes(uint32_t input_num) noexcept {
 8586|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
 8587|       |  unsigned long ret;
 8588|       |  // Search the mask data from least significant bit (LSB)
 8589|       |  // to the most significant bit (MSB) for a set bit (1).
 8590|       |  _BitScanForward(&ret, input_num);
 8591|       |  return (int)ret;
 8592|       |#else   // ADA_REGULAR_VISUAL_STUDIO
 8593|  9.24k|  return __builtin_ctzl(input_num);
 8594|  9.24k|#endif  // ADA_REGULAR_VISUAL_STUDIO
 8595|  9.24k|}
_ZN3ada7helpers24find_next_host_delimiterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 9041|  4.29k|                                                  size_t location) noexcept {
 9042|       |  // first check for short strings in which case we do it naively.
 9043|  4.29k|  if (view.size() - location < 16) {  // slow path
  ------------------
  |  Branch (9043:7): [True: 2.18k, False: 2.11k]
  ------------------
 9044|  5.81k|    for (size_t i = location; i < view.size(); i++) {
  ------------------
  |  Branch (9044:31): [True: 4.68k, False: 1.12k]
  ------------------
 9045|  4.68k|      if (view[i] == ':' || view[i] == '/' || view[i] == '?' ||
  ------------------
  |  Branch (9045:11): [True: 237, False: 4.44k]
  |  Branch (9045:29): [True: 153, False: 4.29k]
  |  Branch (9045:47): [True: 150, False: 4.14k]
  ------------------
 9046|  4.14k|          view[i] == '[') {
  ------------------
  |  Branch (9046:11): [True: 513, False: 3.63k]
  ------------------
 9047|  1.05k|        return i;
 9048|  1.05k|      }
 9049|  4.68k|    }
 9050|  1.12k|    return size_t(view.size());
 9051|  2.18k|  }
 9052|       |  // fast path for long strings (expected to be common)
 9053|  2.11k|  size_t i = location;
 9054|  2.11k|  const __m128i mask1 = _mm_set1_epi8(':');
 9055|  2.11k|  const __m128i mask2 = _mm_set1_epi8('/');
 9056|  2.11k|  const __m128i mask4 = _mm_set1_epi8('?');
 9057|  2.11k|  const __m128i mask5 = _mm_set1_epi8('[');
 9058|       |
 9059|  6.39k|  for (; i + 15 < view.size(); i += 16) {
  ------------------
  |  Branch (9059:10): [True: 5.55k, False: 834]
  ------------------
 9060|  5.55k|    __m128i word = _mm_loadu_si128((const __m128i*)(view.data() + i));
 9061|  5.55k|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 9062|  5.55k|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 9063|  5.55k|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 9064|  5.55k|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 9065|  5.55k|    __m128i m = _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m4, m5));
 9066|  5.55k|    int mask = _mm_movemask_epi8(m);
 9067|  5.55k|    if (mask != 0) {
  ------------------
  |  Branch (9067:9): [True: 1.27k, False: 4.27k]
  ------------------
 9068|  1.27k|      return i + trailing_zeroes(mask);
 9069|  1.27k|    }
 9070|  5.55k|  }
 9071|    834|  if (i < view.size()) {
  ------------------
  |  Branch (9071:7): [True: 537, False: 297]
  ------------------
 9072|    537|    __m128i word =
 9073|    537|        _mm_loadu_si128((const __m128i*)(view.data() + view.length() - 16));
 9074|    537|    __m128i m1 = _mm_cmpeq_epi8(word, mask1);
 9075|    537|    __m128i m2 = _mm_cmpeq_epi8(word, mask2);
 9076|    537|    __m128i m4 = _mm_cmpeq_epi8(word, mask4);
 9077|    537|    __m128i m5 = _mm_cmpeq_epi8(word, mask5);
 9078|    537|    __m128i m = _mm_or_si128(_mm_or_si128(m1, m2), _mm_or_si128(m4, m5));
 9079|    537|    int mask = _mm_movemask_epi8(m);
 9080|    537|    if (mask != 0) {
  ------------------
  |  Branch (9080:9): [True: 126, False: 411]
  ------------------
 9081|    126|      return view.length() - 16 + trailing_zeroes(mask);
 9082|    126|    }
 9083|    537|  }
 9084|    708|  return size_t(view.length());
 9085|    834|}
_ZN3ada3url10parse_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
10124|  14.6k|ada_really_inline bool url::parse_host(std::string_view input) {
10125|  14.6k|  ada_log("parse_host ", input, " [", input.size(), " bytes]");
10126|  14.6k|  if (input.empty()) {
  ------------------
  |  Branch (10126:7): [True: 24, False: 14.5k]
  ------------------
10127|     24|    return is_valid = false;
10128|     24|  }  // technically unnecessary.
10129|       |  // If input starts with U+005B ([), then:
10130|  14.5k|  if (input[0] == '[') {
  ------------------
  |  Branch (10130:7): [True: 687, False: 13.9k]
  ------------------
10131|       |    // If input does not end with U+005D (]), validation error, return failure.
10132|    687|    if (input.back() != ']') {
  ------------------
  |  Branch (10132:9): [True: 153, False: 534]
  ------------------
10133|    153|      return is_valid = false;
10134|    153|    }
10135|    534|    ada_log("parse_host ipv6");
10136|       |
10137|       |    // Return the result of IPv6 parsing input with its leading U+005B ([) and
10138|       |    // trailing U+005D (]) removed.
10139|    534|    input.remove_prefix(1);
10140|    534|    input.remove_suffix(1);
10141|    534|    return parse_ipv6(input);
10142|    687|  }
10143|       |
10144|       |  // If isNotSpecial is true, then return the result of opaque-host parsing
10145|       |  // input.
10146|  13.9k|  if (!is_special()) {
  ------------------
  |  Branch (10146:7): [True: 625, False: 13.2k]
  ------------------
10147|    625|    return parse_opaque_host(input);
10148|    625|  }
10149|       |
10150|       |  // Fast path: try to parse as pure decimal IPv4 first.
10151|  13.2k|  const uint64_t fast_result = checkers::try_parse_ipv4_fast(input);
10152|  13.2k|  if (fast_result < checkers::ipv4_fast_fail) {
  ------------------
  |  Branch (10152:7): [True: 961, False: 12.3k]
  ------------------
10153|    961|    if (!input.empty() && input.back() == '.') {
  ------------------
  |  Branch (10153:9): [True: 961, False: 0]
  |  Branch (10153:27): [True: 24, False: 937]
  ------------------
10154|     24|      host = input.substr(0, input.size() - 1);
10155|    937|    } else {
10156|    937|      host = input;
10157|    937|    }
10158|    961|    host_type = IPV4;
10159|    961|    is_valid = true;
10160|    961|    ada_log("parse_host fast path decimal ipv4");
10161|    961|    return true;
10162|    961|  }
10163|       |  // Let domain be the result of running UTF-8 decode without BOM on the
10164|       |  // percent-decoding of input. Let asciiDomain be the result of running domain
10165|       |  // to ASCII with domain and false. The most common case is an ASCII input, in
10166|       |  // which case we do not need to call the expensive 'to_ascii' if a few
10167|       |  // conditions are met: no '%' and no 'xn-' subsequence.
10168|  12.3k|  const uint8_t forbidden_or_upper =
10169|  12.3k|      unicode::contains_forbidden_domain_code_point_or_upper(input.data(),
10170|  12.3k|                                                             input.size());
10171|  12.3k|  static constexpr std::string_view xn_dash{"xn-", 3};
10172|  12.3k|  if ((forbidden_or_upper & 1) == 0) {
  ------------------
  |  Branch (10172:7): [True: 6.91k, False: 5.41k]
  ------------------
10173|  6.91k|    host = std::string(input);
10174|  6.91k|    if ((forbidden_or_upper & 2) != 0) {
  ------------------
  |  Branch (10174:9): [True: 1.23k, False: 5.67k]
  ------------------
10175|  1.23k|      unicode::to_lower_ascii(host->data(), host->size());
10176|  1.23k|    }
10177|  6.91k|    if (host->find('-') == std::string_view::npos ||
  ------------------
  |  Branch (10177:9): [True: 4.78k, False: 2.12k]
  ------------------
10178|  5.09k|        host->find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (10178:9): [True: 303, False: 1.82k]
  ------------------
10179|  5.09k|      if (checkers::is_ipv4(*host)) {
  ------------------
  |  Branch (10179:11): [True: 1.31k, False: 3.77k]
  ------------------
10180|  1.31k|        ada_log("parse_host fast path ipv4");
10181|  1.31k|        return parse_ipv4(*host);
10182|  1.31k|      }
10183|  3.77k|      ada_log("parse_host fast path ", *host);
10184|  3.77k|      is_valid = true;
10185|  3.77k|      return true;
10186|  5.09k|    }
10187|  6.91k|  } else if (const size_t first_percent = input.find('%');
10188|  5.41k|             first_percent != std::string_view::npos) {
  ------------------
  |  Branch (10188:14): [True: 1.01k, False: 4.40k]
  ------------------
10189|  1.01k|    std::string decoded = unicode::percent_decode(input, first_percent);
10190|  1.01k|    const uint8_t decoded_flags =
10191|  1.01k|        unicode::contains_forbidden_domain_code_point_or_upper(decoded.data(),
10192|  1.01k|                                                               decoded.size());
10193|  1.01k|    if ((decoded_flags & 1) == 0) {
  ------------------
  |  Branch (10193:9): [True: 662, False: 349]
  ------------------
10194|    662|      if ((decoded_flags & 2) != 0) {
  ------------------
  |  Branch (10194:11): [True: 307, False: 355]
  ------------------
10195|    307|        unicode::to_lower_ascii(decoded.data(), decoded.size());
10196|    307|      }
10197|    662|      if (decoded.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (10197:11): [True: 569, False: 93]
  ------------------
10198|    610|          decoded.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (10198:11): [True: 41, False: 52]
  ------------------
10199|    610|        host = std::move(decoded);
10200|    610|        if (checkers::is_ipv4(*host)) {
  ------------------
  |  Branch (10200:13): [True: 278, False: 332]
  ------------------
10201|    278|          return parse_ipv4(*host);
10202|    278|        }
10203|    332|        is_valid = true;
10204|    332|        return true;
10205|    610|      }
10206|    662|    }
10207|  1.01k|  }
10208|  6.62k|  ada_log("parse_host calling to_ascii");
10209|  6.62k|  is_valid = ada::unicode::to_ascii(host, input, input.find('%'));
10210|  6.62k|  if (!is_valid || !host.has_value()) {
  ------------------
  |  Branch (10210:7): [True: 2.13k, False: 4.48k]
  |  Branch (10210:20): [True: 0, False: 4.48k]
  ------------------
10211|  2.13k|    ada_log("parse_host to_ascii returns false");
10212|  2.13k|    return is_valid = false;
10213|  2.13k|  }
10214|  4.48k|  ada_log("parse_host to_ascii succeeded ", *host, " [", host->size(),
10215|  4.48k|          " bytes]");
10216|       |
10217|  4.48k|  if (std::any_of(host->begin(), host->end(),
  ------------------
  |  Branch (10217:7): [True: 0, False: 4.48k]
  ------------------
10218|  4.48k|                  ada::unicode::is_forbidden_domain_code_point)) {
10219|      0|    host = std::nullopt;
10220|      0|    return is_valid = false;
10221|      0|  }
10222|       |
10223|       |  // If asciiDomain ends in a number, then return the result of IPv4 parsing
10224|       |  // asciiDomain.
10225|  4.48k|  if (checkers::is_ipv4(*host)) {
  ------------------
  |  Branch (10225:7): [True: 196, False: 4.29k]
  ------------------
10226|    196|    ada_log("parse_host got ipv4 ", *host);
10227|    196|    return parse_ipv4(*host);
10228|    196|  }
10229|       |
10230|  4.29k|  return true;
10231|  4.48k|}
_ZN3ada7unicode45contains_forbidden_domain_code_point_or_upperEPKcm:
 7055|  40.5k|                                              size_t length) noexcept {
 7056|  40.5k|  size_t i = 0;
 7057|  40.5k|  uint8_t accumulator{};
 7058|   287k|  for (; i + 4 <= length; i += 4) {
  ------------------
  |  Branch (7058:10): [True: 247k, False: 40.5k]
  ------------------
 7059|   247k|    accumulator |=
 7060|   247k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
 7061|   247k|    accumulator |=
 7062|   247k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 1])];
 7063|   247k|    accumulator |=
 7064|   247k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 2])];
 7065|   247k|    accumulator |=
 7066|   247k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i + 3])];
 7067|   247k|  }
 7068|   100k|  for (; i < length; i++) {
  ------------------
  |  Branch (7068:10): [True: 59.6k, False: 40.5k]
  ------------------
 7069|  59.6k|    accumulator |=
 7070|  59.6k|        is_forbidden_domain_code_point_table_or_upper[uint8_t(input[i])];
 7071|  59.6k|  }
 7072|  40.5k|  return accumulator;
 7073|  40.5k|}
_ZN3ada7unicode30is_forbidden_domain_code_pointEc:
 7014|  1.10M|    const char c) noexcept {
 7015|  1.10M|  return is_forbidden_domain_code_point_table[uint8_t(c)];
 7016|  1.10M|}
_ZN3ada7helpers6resizeERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 8578|     20|ada_really_inline void resize(std::string_view& input, size_t pos) noexcept {
 8579|     20|  ADA_ASSERT_TRUE(pos <= input.size());
 8580|     20|  input.remove_suffix(input.size() - pos);
 8581|     20|}
_ZZN3ada6parser14parse_url_implINS_14url_aggregatorELb1EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_ENKUlvE_clEv:
12556|  18.0k|  const auto enforce_max_input_length = [&]() {
12557|  18.0k|    if constexpr (store_values) {
12558|  18.0k|      if (url.is_valid) {
  ------------------
  |  Branch (12558:11): [True: 18.0k, False: 0]
  ------------------
12559|  18.0k|        if constexpr (result_type_is_ada_url_aggregator) {
12560|  18.0k|          if (url.buffer.size() > max_input_length) [[unlikely]] {
  ------------------
  |  Branch (12560:15): [True: 0, False: 18.0k]
  ------------------
12561|      0|            url.is_valid = false;
12562|      0|          }
12563|       |        } else {
12564|       |          if (url.get_href_size() > max_input_length) [[unlikely]] {
12565|       |            url.is_valid = false;
12566|       |          }
12567|       |        }
12568|  18.0k|      }
12569|  18.0k|    }
12570|  18.0k|  };
_ZN3ada7helpers12shorten_pathERNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEENS_6scheme4typeE:
 8540|    976|                                    ada::scheme::type type) {
 8541|       |  // Let path be url's path.
 8542|       |  // If url's scheme is "file", path's size is 1, and path[0] is a normalized
 8543|       |  // Windows drive letter, then return.
 8544|    976|  if (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (8544:7): [True: 184, False: 792]
  ------------------
 8545|    184|      path.find('/', 1) == std::string_view::npos && !path.empty()) {
  ------------------
  |  Branch (8545:7): [True: 162, False: 22]
  |  Branch (8545:54): [True: 70, False: 92]
  ------------------
 8546|     70|    if (checkers::is_normalized_windows_drive_letter(
  ------------------
  |  Branch (8546:9): [True: 2, False: 68]
  ------------------
 8547|     70|            helpers::substring(path, 1))) {
 8548|      2|      return false;
 8549|      2|    }
 8550|     70|  }
 8551|       |
 8552|       |  // Remove path's last item, if any.
 8553|    974|  if (!path.empty()) {
  ------------------
  |  Branch (8553:7): [True: 437, False: 537]
  ------------------
 8554|    437|    size_t slash_loc = path.rfind('/');
 8555|    437|    if (slash_loc != std::string_view::npos) {
  ------------------
  |  Branch (8555:9): [True: 437, False: 0]
  ------------------
 8556|    437|      path.remove_suffix(path.size() - slash_loc);
 8557|    437|      return true;
 8558|    437|    }
 8559|    437|  }
 8560|       |
 8561|    537|  return false;
 8562|    974|}
_ZZN3ada6parser14parse_url_implINS_14url_aggregatorELb0EEET_NSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEPKS3_ENKUlvE_clEv:
12556|    322|  const auto enforce_max_input_length = [&]() {
12557|       |    if constexpr (store_values) {
12558|       |      if (url.is_valid) {
12559|       |        if constexpr (result_type_is_ada_url_aggregator) {
12560|       |          if (url.buffer.size() > max_input_length) [[unlikely]] {
12561|       |            url.is_valid = false;
12562|       |          }
12563|       |        } else {
12564|       |          if (url.get_href_size() > max_input_length) [[unlikely]] {
12565|       |            url.is_valid = false;
12566|       |          }
12567|       |        }
12568|       |      }
12569|       |    }
12570|    322|  };
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_124port_decimal_digit_countEj:
10886|  1.26k|ada_really_inline uint32_t port_decimal_digit_count(uint32_t port) noexcept {
10887|  1.26k|  uint32_t digits = 1;
10888|  1.91k|  while (port >= 10) {
  ------------------
  |  Branch (10888:10): [True: 646, False: 1.26k]
  ------------------
10889|    646|    port /= 10;
10890|    646|    ++digits;
10891|    646|  }
10892|  1.26k|  return digits;
10893|  1.26k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_121append_canonical_portERNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEj:
10896|    415|                                             uint32_t port) {
10897|    415|  buffer += ':';
10898|    415|  char port_buf[5];
10899|    415|  auto [ptr, ec] = std::to_chars(port_buf, port_buf + 5, port);
10900|    415|  (void)ec;
10901|    415|  buffer.append(port_buf, static_cast<size_t>(ptr - port_buf));
10902|    415|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_116authority_has_atEPKhmm:
11044|  4.61k|                                        size_t n) noexcept {
11045|  4.61k|  const size_t rem = n - host_start;
11046|  4.61k|  if (rem == 0) {
  ------------------
  |  Branch (11046:7): [True: 0, False: 4.61k]
  ------------------
11047|      0|    return false;
11048|      0|  }
11049|       |#if ADA_NEON
11050|       |  if (rem >= 16) {
11051|       |    const uint8x16_t w = vld1q_u8(p + host_start);
11052|       |    const uint64_t at = neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('@')));
11053|       |    if (at == 0) {
11054|       |      return false;
11055|       |    }
11056|       |    const uint64_t delim = neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('/'))) |
11057|       |                           neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('?'))) |
11058|       |                           neon_nibble_bits(vceqq_u8(w, vdupq_n_u8('#')));
11059|       |    return delim == 0 || std::countr_zero(at) < std::countr_zero(delim);
11060|       |  }
11061|       |#elif ADA_SSE2
11062|  4.61k|  if (rem >= 16) {
  ------------------
  |  Branch (11062:7): [True: 1.36k, False: 3.25k]
  ------------------
11063|  1.36k|    const __m128i w =
11064|  1.36k|        _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + host_start));
11065|  1.36k|    const uint16_t at = static_cast<uint16_t>(
11066|  1.36k|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('@'))));
11067|  1.36k|    if (at == 0) {
  ------------------
  |  Branch (11067:9): [True: 1.30k, False: 62]
  ------------------
11068|  1.30k|      return false;
11069|  1.30k|    }
11070|     62|    const uint16_t delim = static_cast<uint16_t>(
11071|     62|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('/'))) |
11072|     62|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('?'))) |
11073|     62|        _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('#'))));
11074|     62|    return delim == 0 || std::countr_zero(at) < std::countr_zero(delim);
  ------------------
  |  Branch (11074:12): [True: 18, False: 44]
  |  Branch (11074:26): [True: 8, False: 36]
  ------------------
11075|  1.36k|  }
11076|  3.25k|#endif
11077|  3.25k|  const size_t lim = host_start + rem;
11078|  17.0k|  for (size_t i = host_start; i < lim; ++i) {
  ------------------
  |  Branch (11078:31): [True: 15.5k, False: 1.52k]
  ------------------
11079|  15.5k|    const uint8_t c = p[i];
11080|  15.5k|    if (c == '@') {
  ------------------
  |  Branch (11080:9): [True: 18, False: 15.4k]
  ------------------
11081|     18|      return true;
11082|     18|    }
11083|  15.4k|    if (c == '/' || c == '?' || c == '#') {
  ------------------
  |  Branch (11083:9): [True: 1.66k, False: 13.8k]
  |  Branch (11083:21): [True: 24, False: 13.8k]
  |  Branch (11083:33): [True: 22, False: 13.7k]
  ------------------
11084|  1.70k|      return false;
11085|  1.70k|    }
11086|  15.4k|  }
11087|  1.52k|  return false;
11088|  3.25k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_115scan_plain_hostEPKhmmRmRbS5_:
11095|  12.1k|                                     bool& has_x) noexcept {
11096|  12.1k|  has_upper = false;
11097|  12.1k|  has_x = false;
11098|  12.1k|  size_t i = start;
11099|  12.1k|#if ADA_PARSER_SSSE3
11100|  12.1k|  if (len - start >= 16) {
  ------------------
  |  Branch (11100:7): [True: 5.41k, False: 6.76k]
  ------------------
11101|  5.41k|    const __m128i lo_tbl = nibble_load(k_host_nibbles.low);
11102|  5.41k|    const __m128i hi_tbl = nibble_load(k_host_nibbles.high);
11103|  5.41k|    const __m128i x_splat = _mm_set1_epi8('x');
11104|  5.41k|    auto visit = [&](size_t at) noexcept -> bool {
11105|  5.41k|      const __m128i w =
11106|  5.41k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + at));
11107|  5.41k|      const int up = sse2_uppercase(w);
11108|  5.41k|      const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11109|  5.41k|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11110|  5.41k|      if (mask == 0) {
11111|  5.41k|        if (up != 0) {
11112|  5.41k|          has_upper = true;
11113|  5.41k|        }
11114|  5.41k|        if (xs != 0) {
11115|  5.41k|          has_x = true;
11116|  5.41k|        }
11117|  5.41k|        return false;
11118|  5.41k|      }
11119|  5.41k|      const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11120|  5.41k|      const int valid = (1 << hit) - 1;
11121|  5.41k|      if ((up & valid) != 0) {
11122|  5.41k|        has_upper = true;
11123|  5.41k|      }
11124|  5.41k|      if ((xs & valid) != 0) {
11125|  5.41k|        has_x = true;
11126|  5.41k|      }
11127|  5.41k|      end = at + static_cast<size_t>(hit);
11128|  5.41k|      return true;
11129|  5.41k|    };
11130|  10.9k|    for (; i + 32 <= len; i += 32) {
  ------------------
  |  Branch (11130:12): [True: 7.78k, False: 3.17k]
  ------------------
11131|  7.78k|      if (visit(i) || visit(i + 16)) {
  ------------------
  |  Branch (11131:11): [True: 2.07k, False: 5.70k]
  |  Branch (11131:23): [True: 158, False: 5.55k]
  ------------------
11132|  2.23k|        return k_host_class[b[end]] == 1;
11133|  2.23k|      }
11134|  7.78k|    }
11135|  4.37k|    for (; i + 16 <= len; i += 16) {
  ------------------
  |  Branch (11135:12): [True: 2.59k, False: 1.77k]
  ------------------
11136|  2.59k|      if (visit(i)) {
  ------------------
  |  Branch (11136:11): [True: 1.40k, False: 1.19k]
  ------------------
11137|  1.40k|        return k_host_class[b[end]] == 1;
11138|  1.40k|      }
11139|  2.59k|    }
11140|       |    // Overlapping tail only after a full 16-byte step so the window cannot
11141|       |    // start before `start` (a prior '/' would otherwise look like a host stop).
11142|  1.77k|    if (i > start && i < len) {
  ------------------
  |  Branch (11142:9): [True: 1.77k, False: 0]
  |  Branch (11142:22): [True: 1.72k, False: 56]
  ------------------
11143|  1.72k|      if (visit(len - 16) && end >= i) {
  ------------------
  |  Branch (11143:11): [True: 1.67k, False: 50]
  |  Branch (11143:30): [True: 1.67k, False: 0]
  ------------------
11144|  1.67k|        return k_host_class[b[end]] == 1;
11145|  1.67k|      }
11146|     50|      end = len;
11147|     50|      return true;
11148|  1.72k|    }
11149|  1.77k|  }
11150|       |#elif ADA_SSE2
11151|       |  const __m128i x_splat = _mm_set1_epi8('x');
11152|       |  for (; i + 32 <= len; i += 32) {
11153|       |    for (size_t off = 0; off < 32; off += 16) {
11154|       |      const __m128i w =
11155|       |          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + off));
11156|       |      const int up = sse2_uppercase(w);
11157|       |      const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11158|       |      const int mask = sse2_host_stop(w);
11159|       |      if (mask != 0) {
11160|       |        const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11161|       |        const int valid = (1 << hit) - 1;
11162|       |        if ((up & valid) != 0) {
11163|       |          has_upper = true;
11164|       |        }
11165|       |        if ((xs & valid) != 0) {
11166|       |          has_x = true;
11167|       |        }
11168|       |        end = i + off + static_cast<size_t>(hit);
11169|       |        return k_host_class[b[end]] == 1;
11170|       |      }
11171|       |      if (up != 0) {
11172|       |        has_upper = true;
11173|       |      }
11174|       |      if (xs != 0) {
11175|       |        has_x = true;
11176|       |      }
11177|       |    }
11178|       |  }
11179|       |  for (; i + 16 <= len; i += 16) {
11180|       |    const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11181|       |    const int up = sse2_uppercase(w);
11182|       |    const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11183|       |    const int mask = sse2_host_stop(w);
11184|       |    if (mask != 0) {
11185|       |      const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11186|       |      const int valid = (1 << hit) - 1;
11187|       |      if ((up & valid) != 0) {
11188|       |        has_upper = true;
11189|       |      }
11190|       |      if ((xs & valid) != 0) {
11191|       |        has_x = true;
11192|       |      }
11193|       |      end = i + static_cast<size_t>(hit);
11194|       |      return k_host_class[b[end]] == 1;
11195|       |    }
11196|       |    if (up != 0) {
11197|       |      has_upper = true;
11198|       |    }
11199|       |    if (xs != 0) {
11200|       |      has_x = true;
11201|       |    }
11202|       |  }
11203|       |#elif ADA_NEON
11204|       |  if (len - start >= 16) {
11205|       |    const uint8x16_t lo_tbl = vld1q_u8(k_host_nibbles.low);
11206|       |    const uint8x16_t hi_tbl = vld1q_u8(k_host_nibbles.high);
11207|       |    const uint8x16_t x_splat = vdupq_n_u8('x');
11208|       |    auto visit = [&](size_t at) noexcept -> bool {
11209|       |      const uint8x16_t w = vld1q_u8(b + at);
11210|       |      const uint64_t up = neon_uppercase(w);
11211|       |      const uint64_t xs = neon_nibble_bits(vceqq_u8(w, x_splat));
11212|       |      const uint64_t bits = neon_table_stop(w, lo_tbl, hi_tbl);
11213|       |      if (bits == 0) {
11214|       |        if (up != 0) {
11215|       |          has_upper = true;
11216|       |        }
11217|       |        if (xs != 0) {
11218|       |          has_x = true;
11219|       |        }
11220|       |        return false;
11221|       |      }
11222|       |      const size_t hit = static_cast<size_t>(trailing_zeroes64(bits)) >> 2;
11223|       |      const uint64_t valid = (hit == 0) ? 0 : (uint64_t{1} << (hit * 4)) - 1;
11224|       |      if ((up & valid) != 0) {
11225|       |        has_upper = true;
11226|       |      }
11227|       |      if ((xs & valid) != 0) {
11228|       |        has_x = true;
11229|       |      }
11230|       |      end = at + hit;
11231|       |      return true;
11232|       |    };
11233|       |    for (; i + 32 <= len; i += 32) {
11234|       |      if (visit(i) || visit(i + 16)) {
11235|       |        return k_host_class[b[end]] == 1;
11236|       |      }
11237|       |    }
11238|       |    for (; i + 16 <= len; i += 16) {
11239|       |      if (visit(i)) {
11240|       |        return k_host_class[b[end]] == 1;
11241|       |      }
11242|       |    }
11243|       |    if (i > start && i < len) {
11244|       |      if (visit(len - 16) && end >= i) {
11245|       |        return k_host_class[b[end]] == 1;
11246|       |      }
11247|       |      end = len;
11248|       |      return true;
11249|       |    }
11250|       |  }
11251|       |#endif
11252|  26.5k|  for (; i < len; ++i) {
  ------------------
  |  Branch (11252:10): [True: 26.2k, False: 290]
  ------------------
11253|  26.2k|    const uint8_t c = b[i];
11254|  26.2k|    const uint8_t cls = k_host_class[c];
11255|  26.2k|    if (cls == 1) {
  ------------------
  |  Branch (11255:9): [True: 6.35k, False: 19.9k]
  ------------------
11256|  6.35k|      end = i;
11257|  6.35k|      return true;
11258|  6.35k|    }
11259|  19.9k|    if (cls == 2) {
  ------------------
  |  Branch (11259:9): [True: 176, False: 19.7k]
  ------------------
11260|    176|      return false;
11261|    176|    }
11262|  19.7k|    if (c >= 'A' && c <= 'Z') {
  ------------------
  |  Branch (11262:9): [True: 11.9k, False: 7.81k]
  |  Branch (11262:21): [True: 704, False: 11.2k]
  ------------------
11263|    704|      has_upper = true;
11264|  19.0k|    } else if (c == 'x') {
  ------------------
  |  Branch (11264:16): [True: 2.66k, False: 16.3k]
  ------------------
11265|  2.66k|      has_x = true;
11266|  2.66k|    }
11267|  19.7k|  }
11268|    290|  end = len;
11269|    290|  return true;
11270|  6.81k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_111nibble_loadEPKh:
10925|  17.5k|ADA_PARSER_SIMD __m128i nibble_load(const uint8_t* t) noexcept {
10926|  17.5k|  return _mm_load_si128(reinterpret_cast<const __m128i*>(t));
10927|  17.5k|}
can_parse.cc:_ZZN3ada6parser12_GLOBAL__N_115scan_plain_hostEPKhmmRmRbS5_ENK3$_0clEm:
11104|  17.8k|    auto visit = [&](size_t at) noexcept -> bool {
11105|  17.8k|      const __m128i w =
11106|  17.8k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + at));
11107|  17.8k|      const int up = sse2_uppercase(w);
11108|  17.8k|      const int xs = _mm_movemask_epi8(_mm_cmpeq_epi8(w, x_splat));
11109|  17.8k|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11110|  17.8k|      if (mask == 0) {
  ------------------
  |  Branch (11110:11): [True: 12.5k, False: 5.30k]
  ------------------
11111|  12.5k|        if (up != 0) {
  ------------------
  |  Branch (11111:13): [True: 664, False: 11.8k]
  ------------------
11112|    664|          has_upper = true;
11113|    664|        }
11114|  12.5k|        if (xs != 0) {
  ------------------
  |  Branch (11114:13): [True: 10.0k, False: 2.44k]
  ------------------
11115|  10.0k|          has_x = true;
11116|  10.0k|        }
11117|  12.5k|        return false;
11118|  12.5k|      }
11119|  5.30k|      const int hit = trailing_zeroes32(static_cast<uint32_t>(mask));
11120|  5.30k|      const int valid = (1 << hit) - 1;
11121|  5.30k|      if ((up & valid) != 0) {
  ------------------
  |  Branch (11121:11): [True: 496, False: 4.81k]
  ------------------
11122|    496|        has_upper = true;
11123|    496|      }
11124|  5.30k|      if ((xs & valid) != 0) {
  ------------------
  |  Branch (11124:11): [True: 1.26k, False: 4.03k]
  ------------------
11125|  1.26k|        has_x = true;
11126|  1.26k|      }
11127|  5.30k|      end = at + static_cast<size_t>(hit);
11128|  5.30k|      return true;
11129|  17.8k|    };
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_114sse2_uppercaseEDv2_x:
11012|  17.8k|ada_really_inline int sse2_uppercase(__m128i w) noexcept {
11013|  17.8k|  return _mm_movemask_epi8(_mm_and_si128(
11014|  17.8k|      _mm_cmpgt_epi8(w, _mm_set1_epi8(static_cast<char>('A' - 1))),
11015|  17.8k|      _mm_cmplt_epi8(w, _mm_set1_epi8(static_cast<char>('Z' + 1)))));
11016|  17.8k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_117ssse3_nibble_maskEDv2_xS2_S2_:
10932|  27.8k|                                      __m128i hi_tbl) noexcept {
10933|  27.8k|  const __m128i nibble = _mm_set1_epi8(0x0F);
10934|  27.8k|  const __m128i lo = _mm_and_si128(w, nibble);
10935|  27.8k|  const __m128i hi = _mm_and_si128(_mm_srli_epi16(w, 4), nibble);
10936|  27.8k|  const __m128i hit =
10937|  27.8k|      _mm_and_si128(_mm_shuffle_epi8(lo_tbl, lo), _mm_shuffle_epi8(hi_tbl, hi));
10938|  27.8k|  return _mm_movemask_epi8(_mm_cmpeq_epi8(hit, _mm_setzero_si128())) ^ 0xFFFF;
10939|  27.8k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_117trailing_zeroes32Ej:
10904|  7.54k|ada_really_inline int trailing_zeroes32(uint32_t input_num) noexcept {
10905|       |#ifdef ADA_REGULAR_VISUAL_STUDIO
10906|       |  unsigned long ret;
10907|       |  _BitScanForward(&ret, input_num);
10908|       |  return static_cast<int>(ret);
10909|       |#else
10910|  7.54k|  return __builtin_ctz(input_num);
10911|  7.54k|#endif
10912|  7.54k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_113scan_path_runEPKhRmmRb:
11330|  8.36k|                                   bool& maybe_dot_segment) noexcept {
11331|  8.36k|  const size_t run_start = i;
11332|  8.36k|#if ADA_PARSER_SSSE3
11333|  8.36k|  if (i + 16 <= len) {
  ------------------
  |  Branch (11333:7): [True: 2.69k, False: 5.67k]
  ------------------
11334|  2.69k|    const __m128i lo_tbl = nibble_load(k_path_nibbles.low);
11335|  2.69k|    const __m128i hi_tbl = nibble_load(k_path_nibbles.high);
11336|  4.43k|    for (; i + 32 <= len; i += 32) {
  ------------------
  |  Branch (11336:12): [True: 2.96k, False: 1.47k]
  ------------------
11337|  2.96k|      const __m128i w0 =
11338|  2.96k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11339|  2.96k|      const int m0 = ssse3_nibble_mask(w0, lo_tbl, hi_tbl);
11340|  2.96k|      if (m0 != 0) {
  ------------------
  |  Branch (11340:11): [True: 1.01k, False: 1.94k]
  ------------------
11341|  1.01k|        const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(m0));
11342|  1.01k|        note_dots_in_window(b, i, run_start, w0, (1 << hit_bit) - 1,
11343|  1.01k|                            maybe_dot_segment);
11344|  1.01k|        i += static_cast<size_t>(hit_bit);
11345|  1.01k|        return;
11346|  1.01k|      }
11347|  1.94k|      note_dots_in_window(b, i, run_start, w0, 0xFFFF, maybe_dot_segment);
11348|  1.94k|      const __m128i w1 =
11349|  1.94k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + 16));
11350|  1.94k|      const int m1 = ssse3_nibble_mask(w1, lo_tbl, hi_tbl);
11351|  1.94k|      if (m1 != 0) {
  ------------------
  |  Branch (11351:11): [True: 208, False: 1.73k]
  ------------------
11352|    208|        const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(m1));
11353|    208|        note_dots_in_window(b, i + 16, run_start, w1, (1 << hit_bit) - 1,
11354|    208|                            maybe_dot_segment);
11355|    208|        i += 16 + static_cast<size_t>(hit_bit);
11356|    208|        return;
11357|    208|      }
11358|  1.73k|      note_dots_in_window(b, i + 16, run_start, w1, 0xFFFF, maybe_dot_segment);
11359|  1.73k|    }
11360|  2.08k|    for (; i + 16 <= len; i += 16) {
  ------------------
  |  Branch (11360:12): [True: 1.13k, False: 952]
  ------------------
11361|  1.13k|      const __m128i w =
11362|  1.13k|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11363|  1.13k|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11364|  1.13k|      if (mask != 0) {
  ------------------
  |  Branch (11364:11): [True: 518, False: 616]
  ------------------
11365|    518|        const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(mask));
11366|    518|        note_dots_in_window(b, i, run_start, w, (1 << hit_bit) - 1,
11367|    518|                            maybe_dot_segment);
11368|    518|        i += static_cast<size_t>(hit_bit);
11369|    518|        return;
11370|    518|      }
11371|    616|      note_dots_in_window(b, i, run_start, w, 0xFFFF, maybe_dot_segment);
11372|    616|    }
11373|    952|    if (i > run_start && i < len) {
  ------------------
  |  Branch (11373:9): [True: 952, False: 0]
  |  Branch (11373:26): [True: 668, False: 284]
  ------------------
11374|    668|      const __m128i w =
11375|    668|          _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + len - 16));
11376|    668|      const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);
11377|    668|      if (mask != 0) {
  ------------------
  |  Branch (11377:11): [True: 204, False: 464]
  ------------------
11378|    204|        const size_t hit =
11379|    204|            len - 16 +
11380|    204|            static_cast<size_t>(trailing_zeroes32(static_cast<uint32_t>(mask)));
11381|    204|        if (hit >= i) {
  ------------------
  |  Branch (11381:13): [True: 204, False: 0]
  ------------------
11382|    930|          for (size_t j = i; j < hit; ++j) {
  ------------------
  |  Branch (11382:30): [True: 726, False: 204]
  ------------------
11383|    726|            if (b[j] == '.') {
  ------------------
  |  Branch (11383:17): [True: 198, False: 528]
  ------------------
11384|    198|              note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11385|    198|            }
11386|    726|          }
11387|    204|          i = hit;
11388|    204|          return;
11389|    204|        }
11390|    204|      }
11391|  3.59k|      for (size_t j = i; j < len; ++j) {
  ------------------
  |  Branch (11391:26): [True: 3.12k, False: 464]
  ------------------
11392|  3.12k|        if (b[j] == '.') {
  ------------------
  |  Branch (11392:13): [True: 848, False: 2.28k]
  ------------------
11393|    848|          note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11394|    848|        }
11395|  3.12k|      }
11396|    464|      i = len;
11397|    464|      return;
11398|    668|    }
11399|    952|  }
11400|       |#elif ADA_SSE2
11401|       |  for (; i + 16 <= len; i += 16) {
11402|       |    const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));
11403|       |    const int mask = sse2_path_stop(w);
11404|       |    if (mask != 0) {
11405|       |      const int hit_bit = trailing_zeroes32(static_cast<uint32_t>(mask));
11406|       |      note_dots_in_window(b, i, run_start, w, (1 << hit_bit) - 1,
11407|       |                          maybe_dot_segment);
11408|       |      i += static_cast<size_t>(hit_bit);
11409|       |      return;
11410|       |    }
11411|       |    note_dots_in_window(b, i, run_start, w, 0xFFFF, maybe_dot_segment);
11412|       |  }
11413|       |#elif ADA_NEON
11414|       |  if (i + 16 <= len) {
11415|       |    const uint8x16_t lo_tbl = vld1q_u8(k_path_nibbles.low);
11416|       |    const uint8x16_t hi_tbl = vld1q_u8(k_path_nibbles.high);
11417|       |    for (; i + 32 <= len; i += 32) {
11418|       |      const uint8x16_t w0 = vld1q_u8(b + i);
11419|       |      const uint64_t bits0 = neon_table_stop(w0, lo_tbl, hi_tbl);
11420|       |      if (bits0 != 0) {
11421|       |        const size_t hit_off =
11422|       |            static_cast<size_t>(trailing_zeroes64(bits0)) >> 2;
11423|       |        const uint64_t valid =
11424|       |            (hit_off == 0) ? 0 : (uint64_t{1} << (hit_off * 4)) - 1;
11425|       |        note_dots_in_window_neon(b, i, run_start, w0, valid, maybe_dot_segment);
11426|       |        i += hit_off;
11427|       |        return;
11428|       |      }
11429|       |      note_dots_in_window_neon(b, i, run_start, w0, ~uint64_t{0},
11430|       |                               maybe_dot_segment);
11431|       |      const uint8x16_t w1 = vld1q_u8(b + i + 16);
11432|       |      const uint64_t bits1 = neon_table_stop(w1, lo_tbl, hi_tbl);
11433|       |      if (bits1 != 0) {
11434|       |        const size_t hit_off =
11435|       |            static_cast<size_t>(trailing_zeroes64(bits1)) >> 2;
11436|       |        const uint64_t valid =
11437|       |            (hit_off == 0) ? 0 : (uint64_t{1} << (hit_off * 4)) - 1;
11438|       |        note_dots_in_window_neon(b, i + 16, run_start, w1, valid,
11439|       |                                 maybe_dot_segment);
11440|       |        i += 16 + hit_off;
11441|       |        return;
11442|       |      }
11443|       |      note_dots_in_window_neon(b, i + 16, run_start, w1, ~uint64_t{0},
11444|       |                               maybe_dot_segment);
11445|       |    }
11446|       |    for (; i + 16 <= len; i += 16) {
11447|       |      const uint8x16_t w = vld1q_u8(b + i);
11448|       |      const uint64_t bits = neon_table_stop(w, lo_tbl, hi_tbl);
11449|       |      if (bits != 0) {
11450|       |        const size_t hit_off =
11451|       |            static_cast<size_t>(trailing_zeroes64(bits)) >> 2;
11452|       |        const uint64_t valid =
11453|       |            (hit_off == 0) ? 0 : (uint64_t{1} << (hit_off * 4)) - 1;
11454|       |        note_dots_in_window_neon(b, i, run_start, w, valid, maybe_dot_segment);
11455|       |        i += hit_off;
11456|       |        return;
11457|       |      }
11458|       |      note_dots_in_window_neon(b, i, run_start, w, ~uint64_t{0},
11459|       |                               maybe_dot_segment);
11460|       |    }
11461|       |    if (i > run_start && i < len) {
11462|       |      const uint8x16_t w = vld1q_u8(b + len - 16);
11463|       |      const uint64_t bits = neon_table_stop(w, lo_tbl, hi_tbl);
11464|       |      if (bits != 0) {
11465|       |        const size_t hit =
11466|       |            len - 16 + (static_cast<size_t>(trailing_zeroes64(bits)) >> 2);
11467|       |        if (hit >= i) {
11468|       |          for (size_t j = i; j < hit; ++j) {
11469|       |            if (b[j] == '.') {
11470|       |              note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11471|       |            }
11472|       |          }
11473|       |          i = hit;
11474|       |          return;
11475|       |        }
11476|       |      }
11477|       |      for (size_t j = i; j < len; ++j) {
11478|       |        if (b[j] == '.') {
11479|       |          note_possible_dot_segment(b, j, run_start, maybe_dot_segment);
11480|       |        }
11481|       |      }
11482|       |      i = len;
11483|       |      return;
11484|       |    }
11485|       |  }
11486|       |#endif
11487|  12.9k|  for (; i < len; ++i) {
  ------------------
  |  Branch (11487:10): [True: 8.77k, False: 4.15k]
  ------------------
11488|  8.77k|    const uint8_t c = b[i];
11489|  8.77k|    const uint8_t cls = k_path[c];
11490|  8.77k|    if (cls != 0) {
  ------------------
  |  Branch (11490:9): [True: 1.80k, False: 6.96k]
  ------------------
11491|  1.80k|      return;
11492|  1.80k|    }
11493|  6.96k|    if (c == '.') {
  ------------------
  |  Branch (11493:9): [True: 2.06k, False: 4.89k]
  ------------------
11494|  2.06k|      note_possible_dot_segment(b, i, run_start, maybe_dot_segment);
11495|  2.06k|    }
11496|  6.96k|  }
11497|  5.96k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_119note_dots_in_windowEPKhmmDv2_xiRb:
11287|  6.04k|                                           bool& maybe_dot_segment) noexcept {
11288|  6.04k|  if (maybe_dot_segment || valid_mask == 0) {
  ------------------
  |  Branch (11288:7): [True: 1.72k, False: 4.32k]
  |  Branch (11288:28): [True: 1.12k, False: 3.19k]
  ------------------
11289|  2.84k|    return;
11290|  2.84k|  }
11291|  3.19k|  const int dots =
11292|  3.19k|      _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('.'))) & valid_mask;
11293|  3.19k|  if (dots == 0) {
  ------------------
  |  Branch (11293:7): [True: 1.56k, False: 1.62k]
  ------------------
11294|  1.56k|    return;
11295|  1.56k|  }
11296|  1.62k|  const int slashes =
11297|  1.62k|      _mm_movemask_epi8(_mm_cmpeq_epi8(w, _mm_set1_epi8('/'))) & valid_mask;
11298|  1.62k|  if (((dots & 1) != 0 && (i == run_start || b[i - 1] == '/')) ||
  ------------------
  |  Branch (11298:8): [True: 710, False: 918]
  |  Branch (11298:28): [True: 360, False: 350]
  |  Branch (11298:46): [True: 48, False: 302]
  ------------------
11299|  1.22k|      (dots & (slashes << 1)) != 0) {
  ------------------
  |  Branch (11299:7): [True: 366, False: 854]
  ------------------
11300|    774|    maybe_dot_segment = true;
11301|    774|  }
11302|  1.62k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_125note_possible_dot_segmentEPKhmmRb:
11274|  3.11k|    bool& maybe_dot_segment) noexcept {
11275|  3.11k|  if (pos == run_start || b[pos - 1] == '/') {
  ------------------
  |  Branch (11275:7): [True: 626, False: 2.48k]
  |  Branch (11275:27): [True: 1.28k, False: 1.20k]
  ------------------
11276|  1.91k|    maybe_dot_segment = true;
11277|  1.91k|  }
11278|  3.11k|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_114scan_query_runEPKhRmm:
11640|    772|                                    size_t len) noexcept {
11641|    772|  ADA_SCAN_STOP_RUN(k_query_nibbles, k_query, sse2_query_stop);
  ------------------
  |  |11503|    772|  do {                                                                       \
  |  |11504|    772|    const size_t scan_start = i;                                             \
  |  |11505|    772|    if (i + 16 <= len) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11505:9): [True: 318, False: 454]
  |  |  ------------------
  |  |11506|    318|      const __m128i lo_tbl = nibble_load((nibbles).low);                     \
  |  |11507|    318|      const __m128i hi_tbl = nibble_load((nibbles).high);                    \
  |  |11508|    936|      for (; i + 32 <= len; i += 32) {                                       \
  |  |  ------------------
  |  |  |  Branch (11508:14): [True: 690, False: 246]
  |  |  ------------------
  |  |11509|    690|        const __m128i w0 =                                                   \
  |  |11510|    690|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11511|    690|        const int m0 = ssse3_nibble_mask(w0, lo_tbl, hi_tbl);                \
  |  |11512|    690|        if (m0 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11512:13): [True: 24, False: 666]
  |  |  ------------------
  |  |11513|     24|          i += static_cast<size_t>(                                          \
  |  |11514|     24|              trailing_zeroes32(static_cast<uint32_t>(m0)));                 \
  |  |11515|     24|          return;                                                            \
  |  |11516|     24|        }                                                                    \
  |  |11517|    690|        const __m128i w1 =                                                   \
  |  |11518|    666|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + 16));   \
  |  |11519|    666|        const int m1 = ssse3_nibble_mask(w1, lo_tbl, hi_tbl);                \
  |  |11520|    666|        if (m1 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11520:13): [True: 48, False: 618]
  |  |  ------------------
  |  |11521|     48|          i += 16 + static_cast<size_t>(                                     \
  |  |11522|     48|                        trailing_zeroes32(static_cast<uint32_t>(m1)));       \
  |  |11523|     48|          return;                                                            \
  |  |11524|     48|        }                                                                    \
  |  |11525|    666|      }                                                                      \
  |  |11526|    426|      for (; i + 16 <= len; i += 16) {                                       \
  |  |  ------------------
  |  |  |  Branch (11526:14): [True: 210, False: 216]
  |  |  ------------------
  |  |11527|    210|        const __m128i w =                                                    \
  |  |11528|    210|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11529|    210|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11530|    210|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11530:13): [True: 30, False: 180]
  |  |  ------------------
  |  |11531|     30|          i += static_cast<size_t>(                                          \
  |  |11532|     30|              trailing_zeroes32(static_cast<uint32_t>(mask)));               \
  |  |11533|     30|          return;                                                            \
  |  |11534|     30|        }                                                                    \
  |  |11535|    210|      }                                                                      \
  |  |11536|    246|      if (i > scan_start && i < len) {                                       \
  |  |  ------------------
  |  |  |  Branch (11536:11): [True: 216, False: 0]
  |  |  |  Branch (11536:29): [True: 190, False: 26]
  |  |  ------------------
  |  |11537|    190|        const __m128i w =                                                    \
  |  |11538|    190|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + len - 16)); \
  |  |11539|    190|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11540|    190|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11540:13): [True: 70, False: 120]
  |  |  ------------------
  |  |11541|     70|          const size_t hit = len - 16 +                                      \
  |  |11542|     70|                             static_cast<size_t>(trailing_zeroes32(          \
  |  |11543|     70|                                 static_cast<uint32_t>(mask)));              \
  |  |11544|     70|          if (hit >= i) {                                                    \
  |  |  ------------------
  |  |  |  Branch (11544:15): [True: 70, False: 0]
  |  |  ------------------
  |  |11545|     70|            i = hit;                                                         \
  |  |11546|     70|            return;                                                          \
  |  |11547|     70|          }                                                                  \
  |  |11548|     70|        }                                                                    \
  |  |11549|    190|        i = len;                                                             \
  |  |11550|    120|        return;                                                              \
  |  |11551|    190|      }                                                                      \
  |  |11552|    216|    }                                                                        \
  |  |11553|  1.36k|    for (; i < len; ++i) {                                                   \
  |  |  ------------------
  |  |  |  Branch (11553:12): [True: 1.09k, False: 274]
  |  |  ------------------
  |  |11554|  1.09k|      if ((cls)[b[i]] != 0) {                                                \
  |  |  ------------------
  |  |  |  Branch (11554:11): [True: 206, False: 886]
  |  |  ------------------
  |  |11555|    206|        return;                                                              \
  |  |11556|    206|      }                                                                      \
  |  |11557|  1.09k|    }                                                                        \
  |  |11558|    480|  } while (0)
  |  |  ------------------
  |  |  |  Branch (11558:12): [Folded, False: 274]
  |  |  ------------------
  ------------------
11642|    772|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_113scan_hash_runEPKhRmm:
11645|    818|                                   size_t len) noexcept {
11646|    818|  ADA_SCAN_STOP_RUN(k_hash_nibbles, k_hash, sse2_hash_stop);
  ------------------
  |  |11503|    818|  do {                                                                       \
  |  |11504|    818|    const size_t scan_start = i;                                             \
  |  |11505|    818|    if (i + 16 <= len) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11505:9): [True: 376, False: 442]
  |  |  ------------------
  |  |11506|    376|      const __m128i lo_tbl = nibble_load((nibbles).low);                     \
  |  |11507|    376|      const __m128i hi_tbl = nibble_load((nibbles).high);                    \
  |  |11508|    878|      for (; i + 32 <= len; i += 32) {                                       \
  |  |  ------------------
  |  |  |  Branch (11508:14): [True: 576, False: 302]
  |  |  ------------------
  |  |11509|    576|        const __m128i w0 =                                                   \
  |  |11510|    576|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11511|    576|        const int m0 = ssse3_nibble_mask(w0, lo_tbl, hi_tbl);                \
  |  |11512|    576|        if (m0 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11512:13): [True: 42, False: 534]
  |  |  ------------------
  |  |11513|     42|          i += static_cast<size_t>(                                          \
  |  |11514|     42|              trailing_zeroes32(static_cast<uint32_t>(m0)));                 \
  |  |11515|     42|          return;                                                            \
  |  |11516|     42|        }                                                                    \
  |  |11517|    576|        const __m128i w1 =                                                   \
  |  |11518|    534|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i + 16));   \
  |  |11519|    534|        const int m1 = ssse3_nibble_mask(w1, lo_tbl, hi_tbl);                \
  |  |11520|    534|        if (m1 != 0) {                                                       \
  |  |  ------------------
  |  |  |  Branch (11520:13): [True: 32, False: 502]
  |  |  ------------------
  |  |11521|     32|          i += 16 + static_cast<size_t>(                                     \
  |  |11522|     32|                        trailing_zeroes32(static_cast<uint32_t>(m1)));       \
  |  |11523|     32|          return;                                                            \
  |  |11524|     32|        }                                                                    \
  |  |11525|    534|      }                                                                      \
  |  |11526|    518|      for (; i + 16 <= len; i += 16) {                                       \
  |  |  ------------------
  |  |  |  Branch (11526:14): [True: 226, False: 292]
  |  |  ------------------
  |  |11527|    226|        const __m128i w =                                                    \
  |  |11528|    226|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + i));        \
  |  |11529|    226|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11530|    226|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11530:13): [True: 10, False: 216]
  |  |  ------------------
  |  |11531|     10|          i += static_cast<size_t>(                                          \
  |  |11532|     10|              trailing_zeroes32(static_cast<uint32_t>(mask)));               \
  |  |11533|     10|          return;                                                            \
  |  |11534|     10|        }                                                                    \
  |  |11535|    226|      }                                                                      \
  |  |11536|    302|      if (i > scan_start && i < len) {                                       \
  |  |  ------------------
  |  |  |  Branch (11536:11): [True: 292, False: 0]
  |  |  |  Branch (11536:29): [True: 258, False: 34]
  |  |  ------------------
  |  |11537|    258|        const __m128i w =                                                    \
  |  |11538|    258|            _mm_loadu_si128(reinterpret_cast<const __m128i*>(b + len - 16)); \
  |  |11539|    258|        const int mask = ssse3_nibble_mask(w, lo_tbl, hi_tbl);               \
  |  |11540|    258|        if (mask != 0) {                                                     \
  |  |  ------------------
  |  |  |  Branch (11540:13): [True: 38, False: 220]
  |  |  ------------------
  |  |11541|     38|          const size_t hit = len - 16 +                                      \
  |  |11542|     38|                             static_cast<size_t>(trailing_zeroes32(          \
  |  |11543|     38|                                 static_cast<uint32_t>(mask)));              \
  |  |11544|     38|          if (hit >= i) {                                                    \
  |  |  ------------------
  |  |  |  Branch (11544:15): [True: 38, False: 0]
  |  |  ------------------
  |  |11545|     38|            i = hit;                                                         \
  |  |11546|     38|            return;                                                          \
  |  |11547|     38|          }                                                                  \
  |  |11548|     38|        }                                                                    \
  |  |11549|    258|        i = len;                                                             \
  |  |11550|    220|        return;                                                              \
  |  |11551|    258|      }                                                                      \
  |  |11552|    292|    }                                                                        \
  |  |11553|  1.20k|    for (; i < len; ++i) {                                                   \
  |  |  ------------------
  |  |  |  Branch (11553:12): [True: 792, False: 410]
  |  |  ------------------
  |  |11554|    792|      if ((cls)[b[i]] != 0) {                                                \
  |  |  ------------------
  |  |  |  Branch (11554:11): [True: 66, False: 726]
  |  |  ------------------
  |  |11555|     66|        return;                                                              \
  |  |11556|     66|      }                                                                      \
  |  |11557|    792|    }                                                                        \
  |  |11558|    476|  } while (0)
  |  |  ------------------
  |  |  |  Branch (11558:12): [Folded, False: 410]
  |  |  ------------------
  ------------------
11647|    818|}
can_parse.cc:_ZN3ada6parser12_GLOBAL__N_120path_has_dot_segmentENSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
11651|  1.43k|bool path_has_dot_segment(std::string_view path) noexcept {
11652|  1.43k|  if (path.empty()) {
  ------------------
  |  Branch (11652:7): [True: 0, False: 1.43k]
  ------------------
11653|      0|    return false;
11654|      0|  }
11655|       |  // "." / ".." at the start, with or without a leading '/'.
11656|  1.43k|  const size_t i = (path[0] == '/') ? 1 : 0;
  ------------------
  |  Branch (11656:20): [True: 1.28k, False: 156]
  ------------------
11657|  1.43k|  if (i < path.size() && path[i] == '.') {
  ------------------
  |  Branch (11657:7): [True: 1.43k, False: 0]
  |  Branch (11657:26): [True: 636, False: 800]
  ------------------
11658|    636|    const size_t after = i + 1;
11659|    636|    if (after == path.size() || path[after] == '/' ||
  ------------------
  |  Branch (11659:9): [True: 58, False: 578]
  |  Branch (11659:33): [True: 210, False: 368]
  ------------------
11660|    368|        (after < path.size() && path[after] == '.' &&
  ------------------
  |  Branch (11660:10): [True: 368, False: 0]
  |  Branch (11660:33): [True: 178, False: 190]
  ------------------
11661|    386|         (after + 1 == path.size() || path[after + 1] == '/'))) {
  ------------------
  |  Branch (11661:11): [True: 36, False: 142]
  |  Branch (11661:39): [True: 82, False: 60]
  ------------------
11662|    386|      return true;
11663|    386|    }
11664|    636|  }
11665|  1.05k|  static constexpr std::string_view slash_dot{"/.", 2};
11666|  1.05k|  size_t p = i;
11667|  2.54k|  while ((p = path.find(slash_dot, p)) != std::string_view::npos) {
  ------------------
  |  Branch (11667:10): [True: 2.13k, False: 412]
  ------------------
11668|  2.13k|    const size_t after = p + 2;
11669|  2.13k|    if (after == path.size() || path[after] == '/' ||
  ------------------
  |  Branch (11669:9): [True: 282, False: 1.84k]
  |  Branch (11669:33): [True: 136, False: 1.71k]
  ------------------
11670|  1.71k|        (after < path.size() && path[after] == '.' &&
  ------------------
  |  Branch (11670:10): [True: 1.71k, False: 0]
  |  Branch (11670:33): [True: 572, False: 1.14k]
  ------------------
11671|    638|         (after + 1 == path.size() || path[after + 1] == '/'))) {
  ------------------
  |  Branch (11671:11): [True: 60, False: 512]
  |  Branch (11671:39): [True: 160, False: 352]
  ------------------
11672|    638|      return true;
11673|    638|    }
11674|  1.49k|    p = after;
11675|  1.49k|  }
11676|    412|  return false;
11677|  1.05k|}
_ZN3ada14url_aggregator11copy_schemeERKS0_:
13770|  1.43k|inline void url_aggregator::copy_scheme(const url_aggregator& u) {
13771|  1.43k|  ada_log("url_aggregator::copy_scheme ", u.buffer);
13772|  1.43k|  ADA_ASSERT_TRUE(validate());
13773|       |  // next line could overflow but unsigned arithmetic has well-defined
13774|       |  // overflows.
13775|  1.43k|  uint32_t new_difference = u.components.protocol_end - components.protocol_end;
13776|       |
13777|  1.43k|  type = u.type;
13778|  1.43k|  buffer.erase(0, components.protocol_end);
13779|  1.43k|  buffer.insert(0, u.get_protocol());
13780|  1.43k|  components.protocol_end = u.components.protocol_end;
13781|       |
13782|       |  // No need to update the components
13783|  1.43k|  if (new_difference == 0) {
  ------------------
  |  Branch (13783:7): [True: 154, False: 1.27k]
  ------------------
13784|    154|    return;
13785|    154|  }
13786|       |
13787|  1.27k|  apply_shifted_non_scheme_offsets(components, new_difference);
13788|  1.27k|  ADA_ASSERT_TRUE(validate());
13789|  1.27k|}
can_parse.cc:_ZN12_GLOBAL__N_132apply_shifted_non_scheme_offsetsERN3ada14url_componentsEj:
13648|  42.5k|    ada::url_components& components, uint32_t new_difference) {
13649|  42.5k|  components.username_end += new_difference;
13650|  42.5k|  components.host_start += new_difference;
13651|  42.5k|  components.host_end += new_difference;
13652|  42.5k|  components.pathname_start += new_difference;
13653|  42.5k|  if (components.search_start != ada::url_components::omitted) {
  ------------------
  |  Branch (13653:7): [True: 0, False: 42.5k]
  ------------------
13654|      0|    components.search_start += new_difference;
13655|      0|  }
13656|  42.5k|  if (components.hash_start != ada::url_components::omitted) {
  ------------------
  |  Branch (13656:7): [True: 0, False: 42.5k]
  ------------------
13657|      0|    components.hash_start += new_difference;
13658|      0|  }
13659|  42.5k|}
_ZN3ada14url_aggregator10parse_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14018|  1.85k|ada_really_inline void url_aggregator::parse_path(std::string_view input) {
14019|  1.85k|  ada_log("url_aggregator::parse_path ", input);
14020|  1.85k|  ADA_ASSERT_TRUE(validate());
14021|  1.85k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14022|  1.85k|  std::string tmp_buffer;
14023|  1.85k|  std::string_view internal_input;
14024|  1.85k|  if (unicode::has_tabs_or_newline(input)) {
  ------------------
  |  Branch (14024:7): [True: 0, False: 1.85k]
  ------------------
14025|      0|    tmp_buffer = input;
14026|       |    // Optimization opportunity: Instead of copying and then pruning, we could
14027|       |    // just directly build the string from user_input.
14028|      0|    helpers::remove_ascii_tab_or_newline(tmp_buffer);
14029|      0|    internal_input = tmp_buffer;
14030|  1.85k|  } else {
14031|  1.85k|    internal_input = input;
14032|  1.85k|  }
14033|       |
14034|       |  // If url is special, then:
14035|  1.85k|  if (is_special()) {
  ------------------
  |  Branch (14035:7): [True: 1.85k, False: 0]
  ------------------
14036|  1.85k|    if (internal_input.empty()) {
  ------------------
  |  Branch (14036:9): [True: 73, False: 1.78k]
  ------------------
14037|     73|      update_base_pathname("/");
14038|  1.78k|    } else if ((internal_input[0] == '/') || (internal_input[0] == '\\')) {
  ------------------
  |  Branch (14038:16): [True: 1.78k, False: 0]
  |  Branch (14038:46): [True: 0, False: 0]
  ------------------
14039|  1.78k|      consume_prepared_path(internal_input.substr(1));
14040|  1.78k|    } else {
14041|      0|      consume_prepared_path(internal_input);
14042|      0|    }
14043|  1.85k|  } else if (!internal_input.empty()) {
  ------------------
  |  Branch (14043:14): [True: 0, False: 0]
  ------------------
14044|      0|    if (internal_input[0] == '/') {
  ------------------
  |  Branch (14044:9): [True: 0, False: 0]
  ------------------
14045|      0|      consume_prepared_path(internal_input.substr(1));
14046|      0|    } else {
14047|      0|      consume_prepared_path(internal_input);
14048|      0|    }
14049|      0|  } else {
14050|       |    // Non-special URLs with an empty host can have their paths erased
14051|       |    // Path-only URLs cannot have their paths erased
14052|      0|    if (components.host_start == components.host_end && !has_authority()) {
  ------------------
  |  Branch (14052:9): [True: 0, False: 0]
  |  Branch (14052:57): [True: 0, False: 0]
  ------------------
14053|      0|      update_base_pathname("/");
14054|      0|    }
14055|      0|  }
14056|  1.85k|  ADA_ASSERT_TRUE(validate());
14057|  1.85k|}
_ZN3ada14url_aggregator10parse_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
14137|  29.0k|ada_really_inline bool url_aggregator::parse_host(std::string_view input) {
14138|  29.0k|  ada_log("url_aggregator:parse_host \"", input, "\" [", input.size(),
14139|  29.0k|          " bytes]");
14140|  29.0k|  ADA_ASSERT_TRUE(validate());
14141|  29.0k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
14142|  29.0k|  if (input.empty()) {
  ------------------
  |  Branch (14142:7): [True: 39, False: 29.0k]
  ------------------
14143|     39|    return is_valid = false;
14144|     39|  }  // technically unnecessary.
14145|       |  // If input starts with U+005B ([), then:
14146|  29.0k|  if (input[0] == '[') {
  ------------------
  |  Branch (14146:7): [True: 1.37k, False: 27.6k]
  ------------------
14147|       |    // If input does not end with U+005D (]), validation error, return failure.
14148|  1.37k|    if (input.back() != ']') {
  ------------------
  |  Branch (14148:9): [True: 306, False: 1.06k]
  ------------------
14149|    306|      return is_valid = false;
14150|    306|    }
14151|  1.06k|    ada_log("parse_host ipv6");
14152|       |
14153|       |    // Return the result of IPv6 parsing input with its leading U+005B ([) and
14154|       |    // trailing U+005D (]) removed.
14155|  1.06k|    input.remove_prefix(1);
14156|  1.06k|    input.remove_suffix(1);
14157|  1.06k|    return parse_ipv6(input);
14158|  1.37k|  }
14159|       |
14160|       |  // If isNotSpecial is true, then return the result of opaque-host parsing
14161|       |  // input.
14162|  27.6k|  if (!is_special()) {
  ------------------
  |  Branch (14162:7): [True: 1.25k, False: 26.4k]
  ------------------
14163|  1.25k|    return parse_opaque_host(input);
14164|  1.25k|  }
14165|       |  // Let domain be the result of running UTF-8 decode without BOM on the
14166|       |  // percent-decoding of input. Let asciiDomain be the result of running domain
14167|       |  // to ASCII with domain and false. The most common case is an ASCII input, in
14168|       |  // which case we do not need to call the expensive 'to_ascii' if a few
14169|       |  // conditions are met: no '%' and no 'xn-' subsequence.
14170|       |
14171|       |  // Often, the input does not contain any forbidden code points, and no upper
14172|       |  // case ASCII letter, then we can just copy it to the buffer. We want to
14173|       |  // optimize for such a common case.
14174|       |
14175|       |  // Fast path: try to parse as pure decimal IPv4 first.
14176|  26.4k|  const uint64_t fast_result = checkers::try_parse_ipv4_fast(input);
14177|  26.4k|  if (fast_result < checkers::ipv4_fast_fail) {
  ------------------
  |  Branch (14177:7): [True: 1.23k, False: 25.1k]
  ------------------
14178|  1.23k|    if (!input.empty() && input.back() == '.') {
  ------------------
  |  Branch (14178:9): [True: 1.23k, False: 0]
  |  Branch (14178:27): [True: 46, False: 1.19k]
  ------------------
14179|     46|      update_base_hostname(input.substr(0, input.size() - 1));
14180|  1.19k|    } else {
14181|  1.19k|      update_base_hostname(input);
14182|  1.19k|    }
14183|  1.23k|    host_type = IPV4;
14184|  1.23k|    is_valid = true;
14185|  1.23k|    ada_log("parse_host fast path decimal ipv4");
14186|  1.23k|    ADA_ASSERT_TRUE(validate());
14187|  1.23k|    return true;
14188|  1.23k|  }
14189|  25.1k|  uint8_t is_forbidden_or_upper =
14190|  25.1k|      unicode::contains_forbidden_domain_code_point_or_upper(input.data(),
14191|  25.1k|                                                             input.size());
14192|  25.1k|  static constexpr std::string_view xn_dash{"xn-", 3};
14193|  25.1k|  if ((is_forbidden_or_upper & 1) == 0) {
  ------------------
  |  Branch (14193:7): [True: 14.3k, False: 10.8k]
  ------------------
14194|  14.3k|    if ((is_forbidden_or_upper & 2) != 0) {
  ------------------
  |  Branch (14194:9): [True: 2.51k, False: 11.8k]
  ------------------
14195|  2.51k|      std::string lowered(input);
14196|  2.51k|      unicode::to_lower_ascii(lowered.data(), lowered.size());
14197|  2.51k|      if (lowered.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (14197:11): [True: 2.18k, False: 327]
  ------------------
14198|  2.33k|          lowered.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (14198:11): [True: 144, False: 183]
  ------------------
14199|  2.33k|        update_base_hostname(lowered);
14200|  2.33k|        if (checkers::is_ipv4(lowered)) {
  ------------------
  |  Branch (14200:13): [True: 458, False: 1.87k]
  ------------------
14201|    458|          ada_log("parse_host fast path ipv4");
14202|    458|          return parse_ipv4(lowered, true);
14203|    458|        }
14204|  1.87k|        ada_log("parse_host fast path ", get_hostname());
14205|  1.87k|        is_valid = true;
14206|  1.87k|        return true;
14207|  2.33k|      }
14208|  11.8k|    } else if (input.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (14208:16): [True: 8.05k, False: 3.79k]
  ------------------
14209|  8.49k|               input.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (14209:16): [True: 441, False: 3.34k]
  ------------------
14210|  8.49k|      update_base_hostname(input);
14211|  8.49k|      if (checkers::is_ipv4(input)) {
  ------------------
  |  Branch (14211:11): [True: 2.17k, False: 6.31k]
  ------------------
14212|  2.17k|        ada_log("parse_host fast path ipv4");
14213|  2.17k|        return parse_ipv4(input, true);
14214|  2.17k|      }
14215|  6.31k|      ada_log("parse_host fast path ", get_hostname());
14216|  6.31k|      is_valid = true;
14217|  6.31k|      return true;
14218|  8.49k|    }
14219|  14.3k|  } else if (const size_t first_percent = input.find('%');
14220|  10.8k|             first_percent != std::string_view::npos) {
  ------------------
  |  Branch (14220:14): [True: 2.02k, False: 8.80k]
  ------------------
14221|  2.02k|    std::string decoded = unicode::percent_decode(input, first_percent);
14222|  2.02k|    const uint8_t decoded_flags =
14223|  2.02k|        unicode::contains_forbidden_domain_code_point_or_upper(decoded.data(),
14224|  2.02k|                                                               decoded.size());
14225|  2.02k|    if ((decoded_flags & 1) == 0) {
  ------------------
  |  Branch (14225:9): [True: 1.32k, False: 698]
  ------------------
14226|  1.32k|      if ((decoded_flags & 2) != 0) {
  ------------------
  |  Branch (14226:11): [True: 614, False: 710]
  ------------------
14227|    614|        unicode::to_lower_ascii(decoded.data(), decoded.size());
14228|    614|      }
14229|  1.32k|      if (decoded.find('-') == std::string_view::npos ||
  ------------------
  |  Branch (14229:11): [True: 1.13k, False: 186]
  ------------------
14230|  1.22k|          decoded.find(xn_dash) == std::string_view::npos) {
  ------------------
  |  Branch (14230:11): [True: 82, False: 104]
  ------------------
14231|  1.22k|        update_base_hostname(decoded);
14232|  1.22k|        if (checkers::is_ipv4(decoded)) {
  ------------------
  |  Branch (14232:13): [True: 556, False: 664]
  ------------------
14233|    556|          return parse_ipv4(decoded, true);
14234|    556|        }
14235|    664|        is_valid = true;
14236|    664|        return true;
14237|  1.22k|      }
14238|  1.32k|    }
14239|  2.02k|  }
14240|       |  // We have encountered at least one forbidden code point or the input contains
14241|       |  // 'xn-' (case insensitive), so we need to call 'to_ascii' to perform the full
14242|       |  // conversion.
14243|       |
14244|  13.1k|  ada_log("parse_host calling to_ascii");
14245|  13.1k|  std::optional<std::string> host = std::string(get_hostname());
14246|  13.1k|  is_valid = ada::unicode::to_ascii(host, input, input.find('%'));
14247|  13.1k|  if (!is_valid) {
  ------------------
  |  Branch (14247:7): [True: 4.27k, False: 8.86k]
  ------------------
14248|  4.27k|    ada_log("parse_host to_ascii returns false");
14249|  4.27k|    return is_valid = false;
14250|  4.27k|  }
14251|  8.86k|  ada_log("parse_host to_ascii succeeded ", *host, " [", host->size(),
14252|  8.86k|          " bytes]");
14253|       |
14254|  8.86k|  if (std::ranges::any_of(host.value(),
  ------------------
  |  Branch (14254:7): [True: 0, False: 8.86k]
  ------------------
14255|  8.86k|                          ada::unicode::is_forbidden_domain_code_point)) {
14256|      0|    return is_valid = false;
14257|      0|  }
14258|       |
14259|       |  // If asciiDomain ends in a number, then return the result of IPv4 parsing
14260|       |  // asciiDomain.
14261|  8.86k|  if (checkers::is_ipv4(host.value())) {
  ------------------
  |  Branch (14261:7): [True: 392, False: 8.46k]
  ------------------
14262|    392|    ada_log("parse_host got ipv4 ", *host);
14263|    392|    return parse_ipv4(host.value(), false);
14264|    392|  }
14265|       |
14266|  8.46k|  update_base_hostname(host.value());
14267|  8.46k|  ADA_ASSERT_TRUE(validate());
14268|  8.46k|  return true;
14269|  8.86k|}
_ZN3ada7helpers9substringENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEm:
 8571|  1.83k|                                                       size_t pos) {
 8572|  1.83k|  ADA_ASSERT_TRUE(pos <= input.size());
 8573|       |  // The following is safer but unneeded if we have the above line:
 8574|       |  // return pos > input.size() ? std::string_view() : input.substr(pos);
 8575|  1.83k|  return input.substr(pos);
 8576|  1.83k|}
_ZN3ada14url_aggregator21consume_prepared_pathENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
15088|  9.59k|inline void url_aggregator::consume_prepared_path(std::string_view input) {
15089|  9.59k|  ada_log("url_aggregator::consume_prepared_path ", input);
15090|       |  /***
15091|       |   * This is largely duplicated code from helpers::parse_prepared_path, which is
15092|       |   * unfortunate. This particular function is nearly identical, except that it
15093|       |   * is a method on url_aggregator. The idea is that the trivial path (which is
15094|       |   * very common) merely appends to the buffer. This is the same trivial path as
15095|       |   * with helpers::parse_prepared_path, except that we have the additional check
15096|       |   * for is_at_path(). Otherwise, we grab a copy of the current path and we
15097|       |   * modify it, and then insert it back into the buffer.
15098|       |   */
15099|  9.59k|  uint8_t accumulator = checkers::path_signature(input);
15100|       |  // Let us first detect a trivial case.
15101|       |  // If it is special, we check that we have no dot, no %,  no \ and no
15102|       |  // character needing percent encoding. Otherwise, we check that we have no %,
15103|       |  // no dot, and no character needing percent encoding.
15104|  9.59k|  constexpr uint8_t need_encoding = 1;
15105|  9.59k|  constexpr uint8_t backslash_char = 2;
15106|  9.59k|  constexpr uint8_t dot_char = 4;
15107|  9.59k|  constexpr uint8_t percent_char = 8;
15108|  9.59k|  bool special = type != ada::scheme::NOT_SPECIAL;
15109|  9.59k|  bool may_need_slow_file_handling = (type == ada::scheme::type::FILE &&
  ------------------
  |  Branch (15109:39): [True: 1.61k, False: 7.98k]
  ------------------
15110|  1.61k|                                      checkers::is_windows_drive_letter(input));
  ------------------
  |  Branch (15110:39): [True: 135, False: 1.47k]
  ------------------
15111|  9.59k|  bool trivial_path =
15112|  9.59k|      (special ? (accumulator == 0)
  ------------------
  |  Branch (15112:7): [True: 4.59k, False: 5.00k]
  |  Branch (15112:8): [True: 8.07k, False: 1.52k]
  ------------------
15113|  9.59k|               : ((accumulator & (need_encoding | dot_char | percent_char)) ==
15114|  1.52k|                  0)) &&
15115|  4.59k|      (!may_need_slow_file_handling);
  ------------------
  |  Branch (15115:7): [True: 4.49k, False: 99]
  ------------------
15116|  9.59k|  if (accumulator == dot_char && !may_need_slow_file_handling) {
  ------------------
  |  Branch (15116:7): [True: 1.42k, False: 8.17k]
  |  Branch (15116:34): [True: 1.40k, False: 23]
  ------------------
15117|       |    // '4' means that we have at least one dot, but nothing that requires
15118|       |    // percent encoding or decoding. The only part that is not trivial is
15119|       |    // that we may have single dots and double dots path segments.
15120|       |    // If we have such segments, then we either have a path that begins
15121|       |    // with '.' (easy to check), or we have the sequence './'.
15122|       |    // Note: input cannot be empty, it must at least contain one character ('.')
15123|       |    // Note: we know that '\' is not present.
15124|  1.40k|    if (input[0] != '.') {
  ------------------
  |  Branch (15124:9): [True: 710, False: 692]
  ------------------
15125|    710|      size_t slashdot = 0;
15126|    710|      bool dot_is_file = true;
15127|  5.13k|      for (;;) {
15128|  5.13k|        slashdot = input.find("/.", slashdot);
15129|  5.13k|        if (slashdot == std::string_view::npos) {  // common case
  ------------------
  |  Branch (15129:13): [True: 710, False: 4.42k]
  ------------------
15130|    710|          break;
15131|  4.42k|        } else {  // uncommon
15132|       |          // only three cases matter: /./, /.. or a final /
15133|  4.42k|          slashdot += 2;
15134|  4.42k|          dot_is_file &= !(slashdot == input.size() || input[slashdot] == '.' ||
  ------------------
  |  Branch (15134:28): [True: 212, False: 4.20k]
  |  Branch (15134:56): [True: 2.61k, False: 1.59k]
  ------------------
15135|  1.59k|                           input[slashdot] == '/');
  ------------------
  |  Branch (15135:28): [True: 1.19k, False: 397]
  ------------------
15136|  4.42k|        }
15137|  5.13k|      }
15138|    710|      trivial_path = dot_is_file;
15139|    710|    }
15140|  1.40k|  }
15141|  9.59k|  if (trivial_path && is_at_path()) {
  ------------------
  |  Branch (15141:7): [True: 4.64k, False: 4.95k]
  |  Branch (15141:23): [True: 4.54k, False: 101]
  ------------------
15142|  4.54k|    ada_log("parse_path trivial");
15143|  4.54k|    buffer += '/';
15144|  4.54k|    buffer += input;
15145|  4.54k|    return;
15146|  4.54k|  }
15147|  5.05k|  std::string path = std::string(get_pathname());
15148|       |  // We are going to need to look a bit at the path, but let us see if we can
15149|       |  // ignore percent encoding *and* backslashes *and* percent characters.
15150|       |  // Except for the trivial case, this is likely to capture 99% of paths out
15151|       |  // there.
15152|  5.05k|  bool fast_path =
15153|  5.05k|      (special &&
  ------------------
  |  Branch (15153:8): [True: 3.91k, False: 1.14k]
  ------------------
15154|  3.91k|       (accumulator & (need_encoding | backslash_char | percent_char)) == 0) &&
  ------------------
  |  Branch (15154:8): [True: 1.28k, False: 2.62k]
  ------------------
15155|  1.28k|      (type != ada::scheme::type::FILE);
  ------------------
  |  Branch (15155:7): [True: 1.08k, False: 206]
  ------------------
15156|  5.05k|  if (fast_path) {
  ------------------
  |  Branch (15156:7): [True: 1.08k, False: 3.97k]
  ------------------
15157|  1.08k|    ada_log("parse_prepared_path fast");
15158|       |    // Here we don't need to worry about \ or percent encoding.
15159|       |    // We also do not have a file protocol. We might have dots, however,
15160|       |    // but dots must as appear as '.', and they cannot be encoded because
15161|       |    // the symbol '%' is not present.
15162|  1.08k|    size_t previous_location = 0;  // We start at 0.
15163|  13.7k|    do {
15164|  13.7k|      size_t new_location = input.find('/', previous_location);
15165|       |      // std::string_view path_view = input;
15166|       |      //  We process the last segment separately:
15167|  13.7k|      if (new_location == std::string_view::npos) {
  ------------------
  |  Branch (15167:11): [True: 1.08k, False: 12.7k]
  ------------------
15168|  1.08k|        std::string_view path_view = input.substr(previous_location);
15169|  1.08k|        if (path_view == "..") {  // The path ends with ..
  ------------------
  |  Branch (15169:13): [True: 121, False: 959]
  ------------------
15170|       |          // e.g., if you receive ".." with an empty path, you go to "/".
15171|    121|          if (path.empty()) {
  ------------------
  |  Branch (15171:15): [True: 48, False: 73]
  ------------------
15172|     48|            path = '/';
15173|     48|            update_base_pathname(path);
15174|     48|            return;
15175|     48|          }
15176|       |          // Fast case where we have nothing to do:
15177|     73|          if (path.back() == '/') {
  ------------------
  |  Branch (15177:15): [True: 22, False: 51]
  ------------------
15178|     22|            update_base_pathname(path);
15179|     22|            return;
15180|     22|          }
15181|       |          // If you have the path "/joe/myfriend",
15182|       |          // then you delete 'myfriend'.
15183|     51|          path.resize(path.rfind('/') + 1);
15184|     51|          update_base_pathname(path);
15185|     51|          return;
15186|     73|        }
15187|    959|        path += '/';
15188|    959|        if (path_view != ".") {
  ------------------
  |  Branch (15188:13): [True: 705, False: 254]
  ------------------
15189|    705|          path.append(path_view);
15190|    705|        }
15191|    959|        update_base_pathname(path);
15192|    959|        return;
15193|  12.7k|      } else {
15194|       |        // This is a non-final segment.
15195|  12.7k|        std::string_view path_view =
15196|  12.7k|            input.substr(previous_location, new_location - previous_location);
15197|  12.7k|        previous_location = new_location + 1;
15198|  12.7k|        if (path_view == "..") {
  ------------------
  |  Branch (15198:13): [True: 2.89k, False: 9.81k]
  ------------------
15199|  2.89k|          size_t last_delimiter = path.rfind('/');
15200|  2.89k|          if (last_delimiter != std::string::npos) {
  ------------------
  |  Branch (15200:15): [True: 1.06k, False: 1.82k]
  ------------------
15201|  1.06k|            path.erase(last_delimiter);
15202|  1.06k|          }
15203|  9.81k|        } else if (path_view != ".") {
  ------------------
  |  Branch (15203:20): [True: 7.86k, False: 1.95k]
  ------------------
15204|  7.86k|          path += '/';
15205|  7.86k|          path.append(path_view);
15206|  7.86k|        }
15207|  12.7k|      }
15208|  13.7k|    } while (true);
  ------------------
  |  Branch (15208:14): [True: 12.7k, Folded]
  ------------------
15209|  3.97k|  } else {
15210|  3.97k|    ada_log("parse_path slow");
15211|       |    // we have reached the general case
15212|  3.97k|    bool needs_percent_encoding = (accumulator & 1);
15213|  3.97k|    std::string path_buffer_tmp;
15214|  27.1k|    do {
15215|  27.1k|      size_t location = (special && (accumulator & 2))
  ------------------
  |  Branch (15215:26): [True: 20.1k, False: 6.98k]
  |  Branch (15215:37): [True: 1.80k, False: 18.3k]
  ------------------
15216|  27.1k|                            ? input.find_first_of("/\\")
15217|  27.1k|                            : input.find('/');
15218|  27.1k|      std::string_view path_view = input;
15219|  27.1k|      if (location != std::string_view::npos) {
  ------------------
  |  Branch (15219:11): [True: 23.1k, False: 3.97k]
  ------------------
15220|  23.1k|        path_view.remove_suffix(path_view.size() - location);
15221|  23.1k|        input.remove_prefix(location + 1);
15222|  23.1k|      }
15223|       |      // path_buffer is either path_view or it might point at a percent encoded
15224|       |      // temporary string.
15225|  27.1k|      std::string_view path_buffer =
15226|  27.1k|          (needs_percent_encoding &&
  ------------------
  |  Branch (15226:12): [True: 14.8k, False: 12.3k]
  ------------------
15227|  14.8k|           ada::unicode::percent_encode<false>(
  ------------------
  |  Branch (15227:12): [True: 5.49k, False: 9.33k]
  ------------------
15228|  14.8k|               path_view, character_sets::PATH_PERCENT_ENCODE, path_buffer_tmp))
15229|  27.1k|              ? path_buffer_tmp
15230|  27.1k|              : path_view;
15231|  27.1k|      if (unicode::is_double_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (15231:11): [True: 4.27k, False: 22.8k]
  ------------------
15232|  4.27k|        helpers::shorten_path(path, type);
15233|  4.27k|        if (location == std::string_view::npos) {
  ------------------
  |  Branch (15233:13): [True: 259, False: 4.01k]
  ------------------
15234|    259|          path += '/';
15235|    259|        }
15236|  22.8k|      } else if (unicode::is_single_dot_path_segment(path_buffer) &&
  ------------------
  |  Branch (15236:18): [True: 1.37k, False: 21.4k]
  ------------------
15237|  1.37k|                 (location == std::string_view::npos)) {
  ------------------
  |  Branch (15237:18): [True: 127, False: 1.25k]
  ------------------
15238|    127|        path += '/';
15239|    127|      }
15240|       |      // Otherwise, if path_buffer is not a single-dot path segment, then:
15241|  22.7k|      else if (!unicode::is_single_dot_path_segment(path_buffer)) {
  ------------------
  |  Branch (15241:16): [True: 21.4k, False: 1.25k]
  ------------------
15242|       |        // If url's scheme is "file", url's path is empty, and path_buffer is a
15243|       |        // Windows drive letter, then replace the second code point in
15244|       |        // path_buffer with U+003A (:).
15245|  21.4k|        if (type == ada::scheme::type::FILE && path.empty() &&
  ------------------
  |  Branch (15245:13): [True: 2.25k, False: 19.2k]
  |  Branch (15245:48): [True: 1.04k, False: 1.21k]
  ------------------
15246|  1.04k|            checkers::is_windows_drive_letter(path_buffer)) {
  ------------------
  |  Branch (15246:13): [True: 160, False: 880]
  ------------------
15247|    160|          path += '/';
15248|    160|          path += path_buffer[0];
15249|    160|          path += ':';
15250|    160|          path_buffer.remove_prefix(2);
15251|    160|          path.append(path_buffer);
15252|  21.3k|        } else {
15253|       |          // Append path_buffer to url's path.
15254|  21.3k|          path += '/';
15255|  21.3k|          path.append(path_buffer);
15256|  21.3k|        }
15257|  21.4k|      }
15258|  27.1k|      if (location == std::string_view::npos) {
  ------------------
  |  Branch (15258:11): [True: 3.97k, False: 23.1k]
  ------------------
15259|  3.97k|        update_base_pathname(path);
15260|  3.97k|        return;
15261|  3.97k|      }
15262|  27.1k|    } while (true);
  ------------------
  |  Branch (15262:14): [True: 23.1k, Folded]
  ------------------
15263|  3.97k|  }
15264|  5.05k|}
_ZN3ada8checkers7is_ipv4ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
   12|  31.0k|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|  31.0k|  if (!last_label_may_be_a_number(view)) {
  ------------------
  |  Branch (14:7): [True: 22.3k, False: 8.78k]
  ------------------
   15|  22.3k|    return false;
   16|  22.3k|  }
   17|       |  // If the address ends with a dot, we need to prune it (special case).
   18|  8.78k|  if (view.ends_with('.')) {
  ------------------
  |  Branch (18:7): [True: 450, False: 8.33k]
  ------------------
   19|    450|    view.remove_suffix(1);
   20|    450|  }
   21|       |  // From the last character, find the last dot.
   22|  8.78k|  size_t last_dot = view.rfind('.');
   23|  8.78k|  if (last_dot != std::string_view::npos) {
  ------------------
  |  Branch (23:7): [True: 2.61k, False: 6.16k]
  ------------------
   24|       |    // We have at least one dot.
   25|  2.61k|    view.remove_prefix(last_dot + 1);
   26|  2.61k|  }
   27|       |  /** Optimization opportunity: we have basically identified the last number of
   28|       |     the ipv4 if we return true here. We might as well parse it and have at
   29|       |     least one number parsed when we get to parse_ipv4. */
   30|  8.78k|  if (std::ranges::all_of(view, ada::checkers::is_digit)) {
  ------------------
  |  Branch (30:7): [True: 4.06k, False: 4.71k]
  ------------------
   31|  4.06k|    return true;
   32|  4.06k|  }
   33|       |  // It could be hex (0x), but not if there is a single character.
   34|  4.71k|  if (view.size() == 1) {
  ------------------
  |  Branch (34:7): [True: 0, False: 4.71k]
  ------------------
   35|      0|    return false;
   36|      0|  }
   37|       |  // It must start with 0x.
   38|  4.71k|  if (!view.starts_with("0x")) {
  ------------------
  |  Branch (38:7): [True: 2.34k, False: 2.37k]
  ------------------
   39|  2.34k|    return false;
   40|  2.34k|  }
   41|       |  // We must allow "0x".
   42|  2.37k|  if (view.size() == 2) {
  ------------------
  |  Branch (42:7): [True: 270, False: 2.10k]
  ------------------
   43|    270|    return true;
   44|    270|  }
   45|       |  // We have 0x followed by some characters, we need to check that they are
   46|       |  // hexadecimals.
   47|  2.10k|  view.remove_prefix(2);
   48|  2.10k|  return std::ranges::all_of(view, ada::unicode::is_lowercase_hex);
   49|  2.37k|}
_ZN3ada7unicode16is_lowercase_hexEc:
 7174|  25.7k|ada_really_inline constexpr bool is_lowercase_hex(const char c) noexcept {
 7175|  25.7k|  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
  ------------------
  |  Branch (7175:11): [True: 25.7k, False: 0]
  |  Branch (7175:23): [True: 13.5k, False: 12.2k]
  |  Branch (7175:37): [True: 12.2k, False: 0]
  |  Branch (7175:49): [True: 11.1k, False: 1.06k]
  ------------------
 7176|  25.7k|}
_ZN3ada3url12parse_schemeILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
10026|  20.7k|ada_really_inline bool url::parse_scheme(const std::string_view input) {
10027|  20.7k|  auto parsed_type = ada::scheme::get_scheme_type(input);
10028|  20.7k|  bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
10029|       |  /**
10030|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
10031|       |   *http, https), in which case, we can go really fast.
10032|       |   **/
10033|  20.7k|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (10033:7): [True: 9.78k, False: 10.9k]
  ------------------
10034|       |    if constexpr (has_state_override) {
10035|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
10036|       |      // then return.
10037|       |      if (is_special() != is_input_special) {
10038|       |        return false;
10039|       |      }
10040|       |
10041|       |      // If url includes credentials or has a non-null port, and buffer is
10042|       |      // "file", then return.
10043|       |      if ((has_credentials() || port.has_value()) &&
10044|       |          parsed_type == ada::scheme::type::FILE) {
10045|       |        return false;
10046|       |      }
10047|       |
10048|       |      // If url's scheme is "file" and its host is an empty host, then return.
10049|       |      // An empty host is the empty string.
10050|       |      if (type == ada::scheme::type::FILE && host.has_value() &&
10051|       |          host->empty()) {
10052|       |        return false;
10053|       |      }
10054|       |    }
10055|       |
10056|  9.78k|    type = parsed_type;
10057|       |
10058|       |    if constexpr (has_state_override) {
10059|       |      // This is uncommon.
10060|       |      uint16_t urls_scheme_port = get_special_port();
10061|       |
10062|       |      if (urls_scheme_port) {
10063|       |        // If url's port is url's scheme's default port, then set url's port to
10064|       |        // null.
10065|       |        if (port.has_value() && *port == urls_scheme_port) {
10066|       |          port = std::nullopt;
10067|       |        }
10068|       |      }
10069|       |    }
10070|  10.9k|  } else {  // slow path
10071|  10.9k|    std::string _buffer(input);
10072|       |    // Next function is only valid if the input is ASCII and returns false
10073|       |    // otherwise, but it seems that we always have ascii content so we do not
10074|       |    // need to check the return value.
10075|       |    // bool is_ascii =
10076|  10.9k|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
10077|       |
10078|       |    if constexpr (has_state_override) {
10079|       |      // The state-override validation errors below ("return" in the WHATWG URL
10080|       |      // parser) leave the URL unchanged. The setter contract is
10081|       |      // "true on success, false if the scheme is invalid" -- the fast path
10082|       |      // above already returns false here, so the slow path must agree.
10083|       |
10084|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
10085|       |      // then return. If url's scheme is not a special scheme and buffer is a
10086|       |      // special scheme, then return.
10087|       |      if (is_special() != ada::scheme::is_special(_buffer)) {
10088|       |        return false;
10089|       |      }
10090|       |
10091|       |      // If url includes credentials or has a non-null port, and buffer is
10092|       |      // "file", then return.
10093|       |      if ((has_credentials() || port.has_value()) && _buffer == "file") {
10094|       |        return false;
10095|       |      }
10096|       |
10097|       |      // If url's scheme is "file" and its host is an empty host, then return.
10098|       |      // An empty host is the empty string.
10099|       |      if (type == ada::scheme::type::FILE && host.has_value() &&
10100|       |          host->empty()) {
10101|       |        return false;
10102|       |      }
10103|       |    }
10104|       |
10105|  10.9k|    set_scheme(std::move(_buffer));
10106|       |
10107|       |    if constexpr (has_state_override) {
10108|       |      // This is uncommon.
10109|       |      uint16_t urls_scheme_port = get_special_port();
10110|       |
10111|       |      if (urls_scheme_port) {
10112|       |        // If url's port is url's scheme's default port, then set url's port to
10113|       |        // null.
10114|       |        if (port.has_value() && *port == urls_scheme_port) {
10115|       |          port = std::nullopt;
10116|       |        }
10117|       |      }
10118|       |    }
10119|  10.9k|  }
10120|       |
10121|  20.7k|  return true;
10122|  20.7k|}
_ZN3ada14url_aggregator23parse_scheme_with_colonILb0EEEbNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE:
13666|  41.2k|    const std::string_view input_with_colon) {
13667|  41.2k|  ada_log("url_aggregator::parse_scheme_with_colon ", input_with_colon);
13668|  41.2k|  ADA_ASSERT_TRUE(validate());
13669|  41.2k|  ADA_ASSERT_TRUE(!helpers::overlaps(input_with_colon, buffer));
13670|  41.2k|  std::string_view input{input_with_colon};
13671|  41.2k|  input.remove_suffix(1);
13672|  41.2k|  auto parsed_type = ada::scheme::get_scheme_type(input);
13673|  41.2k|  const bool is_input_special = (parsed_type != ada::scheme::NOT_SPECIAL);
13674|       |  /**
13675|       |   * In the common case, we will immediately recognize a special scheme (e.g.,
13676|       |   *http, https), in which case, we can go really fast.
13677|       |   **/
13678|  41.2k|  if (is_input_special) {  // fast path!!!
  ------------------
  |  Branch (13678:7): [True: 19.4k, False: 21.8k]
  ------------------
13679|       |    if constexpr (has_state_override) {
13680|       |      // If url's scheme is not a special scheme and buffer is a special scheme,
13681|       |      // then return.
13682|       |      if (is_special() != is_input_special) {
13683|       |        return false;
13684|       |      }
13685|       |
13686|       |      // If url includes credentials or has a non-null port, and buffer is
13687|       |      // "file", then return.
13688|       |      if ((has_credentials() || components.port != url_components::omitted) &&
13689|       |          parsed_type == ada::scheme::type::FILE) {
13690|       |        return false;
13691|       |      }
13692|       |
13693|       |      // If url's scheme is "file" and its host is an empty host, then return.
13694|       |      // An empty host is the empty string.
13695|       |      if (type == ada::scheme::type::FILE &&
13696|       |          components.host_start == components.host_end) {
13697|       |        return false;
13698|       |      }
13699|       |    }
13700|       |
13701|  19.4k|    type = parsed_type;
13702|  19.4k|    set_scheme_from_view_with_colon(input_with_colon);
13703|       |
13704|       |    if constexpr (has_state_override) {
13705|       |      // This is uncommon.
13706|       |      uint16_t urls_scheme_port = get_special_port();
13707|       |
13708|       |      if (urls_scheme_port) {
13709|       |        // If url's port is url's scheme's default port, then set url's port to
13710|       |        // null.
13711|       |        if (components.port == urls_scheme_port) {
13712|       |          clear_port();
13713|       |        }
13714|       |      }
13715|       |    }
13716|  21.8k|  } else {  // slow path
13717|  21.8k|    std::string _buffer(input);
13718|       |    // Next function is only valid if the input is ASCII and returns false
13719|       |    // otherwise, but it seems that we always have ascii content so we do not
13720|       |    // need to check the return value.
13721|  21.8k|    unicode::to_lower_ascii(_buffer.data(), _buffer.size());
13722|       |
13723|       |    if constexpr (has_state_override) {
13724|       |      // The state-override validation errors below ("return" in the WHATWG URL
13725|       |      // parser) leave the URL unchanged. The setter contract is
13726|       |      // "true on success, false if the scheme is invalid" -- the fast path
13727|       |      // above already returns false here, so the slow path must agree.
13728|       |
13729|       |      // If url's scheme is a special scheme and buffer is not a special scheme,
13730|       |      // then return. If url's scheme is not a special scheme and buffer is a
13731|       |      // special scheme, then return.
13732|       |      if (is_special() != ada::scheme::is_special(_buffer)) {
13733|       |        return false;
13734|       |      }
13735|       |
13736|       |      // If url includes credentials or has a non-null port, and buffer is
13737|       |      // "file", then return.
13738|       |      if ((has_credentials() || components.port != url_components::omitted) &&
13739|       |          _buffer == "file") {
13740|       |        return false;
13741|       |      }
13742|       |
13743|       |      // If url's scheme is "file" and its host is an empty host, then return.
13744|       |      // An empty host is the empty string.
13745|       |      if (type == ada::scheme::type::FILE &&
13746|       |          components.host_start == components.host_end) {
13747|       |        return false;
13748|       |      }
13749|       |    }
13750|       |
13751|  21.8k|    set_scheme(_buffer);
13752|       |
13753|       |    if constexpr (has_state_override) {
13754|       |      // This is uncommon.
13755|       |      uint16_t urls_scheme_port = get_special_port();
13756|       |
13757|       |      if (urls_scheme_port) {
13758|       |        // If url's port is url's scheme's default port, then set url's port to
13759|       |        // null.
13760|       |        if (components.port == urls_scheme_port) {
13761|       |          clear_port();
13762|       |        }
13763|       |      }
13764|       |    }
13765|  21.8k|  }
13766|  41.2k|  ADA_ASSERT_TRUE(validate());
13767|  41.2k|  return true;
13768|  41.2k|}
_ZN3ada14url_aggregator31set_scheme_from_view_with_colonENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
13792|  19.4k|    std::string_view new_scheme_with_colon) {
13793|  19.4k|  ada_log("url_aggregator::set_scheme_from_view_with_colon ",
13794|  19.4k|          new_scheme_with_colon);
13795|  19.4k|  ADA_ASSERT_TRUE(validate());
13796|  19.4k|  ADA_ASSERT_TRUE(!new_scheme_with_colon.empty() &&
13797|  19.4k|                  new_scheme_with_colon.back() == ':');
13798|       |  // next line could overflow but unsigned arithmetic has well-defined
13799|       |  // overflows.
13800|  19.4k|  uint32_t new_difference =
13801|  19.4k|      uint32_t(new_scheme_with_colon.size()) - components.protocol_end;
13802|       |
13803|  19.4k|  if (buffer.empty()) {
  ------------------
  |  Branch (13803:7): [True: 19.4k, False: 0]
  ------------------
13804|  19.4k|    buffer.append(new_scheme_with_colon);
13805|  19.4k|  } else {
13806|      0|    buffer.erase(0, components.protocol_end);
13807|      0|    buffer.insert(0, new_scheme_with_colon);
13808|      0|  }
13809|  19.4k|  components.protocol_end += new_difference;
13810|       |
13811|  19.4k|  apply_shifted_non_scheme_offsets(components, new_difference);
13812|  19.4k|  ADA_ASSERT_TRUE(validate());
13813|  19.4k|}
_ZN3ada14url_aggregator10set_schemeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
13815|  21.8k|inline void url_aggregator::set_scheme(std::string_view new_scheme) {
13816|  21.8k|  ada_log("url_aggregator::set_scheme ", new_scheme);
13817|  21.8k|  ADA_ASSERT_TRUE(validate());
13818|  21.8k|  ADA_ASSERT_TRUE(new_scheme.empty() || new_scheme.back() != ':');
13819|       |  // next line could overflow but unsigned arithmetic has well-defined
13820|       |  // overflows.
13821|  21.8k|  uint32_t new_difference =
13822|  21.8k|      uint32_t(new_scheme.size()) - components.protocol_end + 1;
13823|       |
13824|  21.8k|  type = ada::scheme::get_scheme_type(new_scheme);
13825|  21.8k|  if (buffer.empty()) {
  ------------------
  |  Branch (13825:7): [True: 21.8k, False: 0]
  ------------------
13826|  21.8k|    buffer.append(helpers::concat(new_scheme, ":"));
13827|  21.8k|  } else {
13828|      0|    buffer.erase(0, components.protocol_end);
13829|      0|    buffer.insert(0, helpers::concat(new_scheme, ":"));
13830|      0|  }
13831|  21.8k|  components.protocol_end = uint32_t(new_scheme.size() + 1);
13832|       |
13833|  21.8k|  apply_shifted_non_scheme_offsets(components, new_difference);
13834|  21.8k|  ADA_ASSERT_TRUE(validate());
13835|  21.8k|}

_ZN3ada37url_pattern_compile_component_optionsC2ENSt3__18optionalIcEES3_:
 5809|      6|      : delimiter(new_delimiter), prefix(new_prefix) {}
_ZN3ada14character_sets6bit_atEPKhh:
 1081|   341k|ada_really_inline constexpr bool bit_at(const uint8_t a[], const uint8_t i) {
 1082|   341k|  return !!(a[i >> 3] & (1 << (i & 7)));
 1083|   341k|}
_ZN2tl10unexpectedIN3ada6errorsEEC2EOS2_:
 2196|  96.4k|  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:
 3682|  24.1k|      : impl_base(unexpect, std::move(e.value())),
 3683|  24.1k|        ctor_base(detail::default_constructor_tag{}) {}
_ZNR2tl10unexpectedIN3ada6errorsEE5valueEv:
 2210|  48.2k|  TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_:
 2668|  24.1k|      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail26expected_default_ctor_baseIN3ada3urlENS2_6errorsELb1EEC2ENS0_23default_constructor_tagE:
 3286|  46.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_:
 3768|  22.4k|      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_:
 3641|  22.4k|      : impl_base(in_place, std::forward<Args>(args)...),
 3642|  22.4k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_:
 2656|  22.4k|      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN3ada3urlC2EOS0_:
 5051|  22.4k|  url(url&& u) noexcept = default;
_ZN3ada8url_baseD2Ev:
 1700|   192k|  virtual ~url_base() = default;
_ZN3ada3urlD2Ev:
 5054|  68.9k|  ~url() override = default;
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IS3_TnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_OT_EE5valueEvE4typeELPv0ETnPNS7_IXsr3std14is_convertibleIS9_S3_EE5valueEvE4typeELSD_0EEEONS_10unexpectedIS8_EE:
 3682|  24.1k|      : impl_base(unexpect, std::move(e.value())),
 3683|  24.1k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS4_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS4_DpOT_EE5valueEvE4typeELPv0EEENS_10unexpect_tESB_:
 2668|  24.1k|      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
_ZN2tl6detail26expected_default_ctor_baseIN3ada14url_aggregatorENS2_6errorsELb1EEC2ENS0_23default_constructor_tagE:
 3286|  46.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_:
 3768|  22.4k|      : expected(in_place, std::forward<U>(v)) {}
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEC2IJS2_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS2_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESA_:
 3641|  22.4k|      : impl_base(in_place, std::forward<Args>(args)...),
 3642|  22.4k|        ctor_base(detail::default_constructor_tag{}) {}
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EEC2IJS3_ETnPNSt3__19enable_ifIXsr3std16is_constructibleIS3_DpOT_EE5valueEvE4typeELPv0EEENS_10in_place_tESB_:
 2656|  22.4k|      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
_ZN3ada14url_aggregatorC2EOS0_:
 7836|  22.4k|  url_aggregator(url_aggregator&& u) noexcept = default;
_ZN3ada14url_aggregatorD2Ev:
 7839|   123k|  ~url_aggregator() override = default;
_ZN3ada6scheme15get_scheme_typeENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 6851|   112k|constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept {
 6852|   112k|  if (scheme.empty()) {
  ------------------
  |  Branch (6852:7): [True: 0, False: 112k]
  ------------------
 6853|      0|    return ada::scheme::NOT_SPECIAL;
 6854|      0|  }
 6855|   112k|  int hash_value = (2 * scheme.size() + (unsigned)(scheme[0])) & 7;
 6856|   112k|  const std::string_view target = details::is_special_list[hash_value];
 6857|   112k|  if (scheme.size() == target.size() &&
  ------------------
  |  Branch (6857:7): [True: 89.1k, False: 23.1k]
  ------------------
 6858|  89.1k|      details::branchless_load5(scheme.data(), scheme.size()) ==
  ------------------
  |  Branch (6858:7): [True: 60.2k, False: 28.9k]
  ------------------
 6859|  89.1k|          details::scheme_keys[hash_value]) {
 6860|  60.2k|    return ada::scheme::type(hash_value);
 6861|  60.2k|  } else {
 6862|  52.1k|    return ada::scheme::NOT_SPECIAL;
 6863|  52.1k|  }
 6864|   112k|}
_ZN3ada6scheme7details16branchless_load5EPKcm:
 6792|  89.1k|inline uint64_t branchless_load5(const char* p, size_t n) {
 6793|  89.1k|  uint64_t input = (uint8_t)p[0];
 6794|  89.1k|  input |= ((uint64_t)(uint8_t)p[n > 1] << 8) & (0 - (uint64_t)(n > 1));
 6795|  89.1k|  input |= ((uint64_t)(uint8_t)p[(n > 2) * 2] << 16) & (0 - (uint64_t)(n > 2));
 6796|  89.1k|  input |= ((uint64_t)(uint8_t)p[(n > 3) * 3] << 24) & (0 - (uint64_t)(n > 3));
 6797|  89.1k|  input |= ((uint64_t)(uint8_t)p[(n > 4) * 4] << 32) & (0 - (uint64_t)(n > 4));
 6798|  89.1k|  return input;
 6799|  89.1k|}
_ZN3ada14url_aggregatorC2Ev:
 7834|   101k|  url_aggregator() = default;
_ZN3ada14url_componentsC2Ev:
 4960|   101k|  url_components() = default;
_ZN3ada14url_aggregatoraSEOS0_:
 7837|  11.6k|  url_aggregator& operator=(url_aggregator&& u) noexcept = default;
_ZN3ada8checkers19try_parse_ipv4_fastENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1453|  46.3k|try_parse_ipv4_fast(std::string_view input) noexcept {
 1454|  46.3k|  const size_t len = input.size();
 1455|       |  // Shortest pure decimal: "0.0.0.0" (7). Longest + trailing dot: 16.
 1456|  46.3k|  if (len < 7 || len > 16) [[unlikely]] {
  ------------------
  |  Branch (1456:7): [True: 23.0k, False: 23.3k]
  |  Branch (1456:18): [True: 9.96k, False: 13.3k]
  ------------------
 1457|  33.0k|    return ipv4_fast_fail;
 1458|  33.0k|  }
 1459|  13.3k|  const char* data = input.data();
 1460|       |
 1461|       |#if defined(ADA_AVX512) && defined(__AVX512VBMI2__)
 1462|       |  return detail::try_parse_ipv4_avx512(data, len);
 1463|       |#else
 1464|  13.3k|  return detail::parse_ipv4_decimal_scalar(data, data + len);
 1465|  46.3k|#endif
 1466|  46.3k|}
_ZN3ada8checkers6detail25parse_ipv4_decimal_scalarEPKcS3_:
 1294|  13.3k|parse_ipv4_decimal_scalar(const char* p, const char* pend) noexcept {
 1295|  13.3k|  uint32_t ipv4 = 0;
 1296|  29.2k|  for (int i = 0; i < 4; ++i) {
  ------------------
  |  Branch (1296:19): [True: 25.7k, False: 3.52k]
  ------------------
 1297|  25.7k|    if (p == pend) [[unlikely]] {
  ------------------
  |  Branch (1297:9): [True: 13, False: 25.6k]
  ------------------
 1298|     13|      return ipv4_fast_fail;
 1299|     13|    }
 1300|  25.6k|    uint32_t val;
 1301|  25.6k|    char c = *p;
 1302|  25.6k|    if (c >= '0' && c <= '9') [[likely]] {
  ------------------
  |  Branch (1302:9): [True: 23.5k, False: 2.16k]
  |  Branch (1302:21): [True: 19.2k, False: 4.28k]
  ------------------
 1303|  19.2k|      val = static_cast<uint32_t>(c - '0');
 1304|  19.2k|      ++p;
 1305|  19.2k|    } else {
 1306|  6.44k|      return ipv4_fast_fail;
 1307|  6.44k|    }
 1308|  19.2k|    if (p < pend) {
  ------------------
  |  Branch (1308:9): [True: 17.3k, False: 1.92k]
  ------------------
 1309|  17.3k|      c = *p;
 1310|  17.3k|      if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1310:11): [True: 7.20k, False: 10.1k]
  |  Branch (1310:23): [True: 5.87k, False: 1.32k]
  ------------------
 1311|  5.87k|        if (val == 0) [[unlikely]] {
  ------------------
  |  Branch (1311:13): [True: 531, False: 5.34k]
  ------------------
 1312|    531|          return ipv4_fast_fail;
 1313|    531|        }
 1314|  5.34k|        val = val * 10u + static_cast<uint32_t>(c - '0');
 1315|  5.34k|        ++p;
 1316|  5.34k|        if (p < pend) {
  ------------------
  |  Branch (1316:13): [True: 4.70k, False: 637]
  ------------------
 1317|  4.70k|          c = *p;
 1318|  4.70k|          if (c >= '0' && c <= '9') {
  ------------------
  |  Branch (1318:15): [True: 3.28k, False: 1.41k]
  |  Branch (1318:27): [True: 3.13k, False: 149]
  ------------------
 1319|  3.13k|            val = val * 10u + static_cast<uint32_t>(c - '0');
 1320|  3.13k|            ++p;
 1321|  3.13k|            if (val > 255u) [[unlikely]] {
  ------------------
  |  Branch (1321:17): [True: 830, False: 2.30k]
  ------------------
 1322|    830|              return ipv4_fast_fail;
 1323|    830|            }
 1324|  3.13k|          }
 1325|  4.70k|        }
 1326|  5.34k|      }
 1327|  17.3k|    }
 1328|  17.8k|    ipv4 = (ipv4 << 8) | val;
 1329|  17.8k|    if (i < 3) {
  ------------------
  |  Branch (1329:9): [True: 14.3k, False: 3.52k]
  ------------------
 1330|  14.3k|      if (p == pend || *p != '.') [[unlikely]] {
  ------------------
  |  Branch (1330:11): [True: 357, False: 14.0k]
  |  Branch (1330:24): [True: 1.66k, False: 12.3k]
  ------------------
 1331|  2.02k|        return ipv4_fast_fail;
 1332|  2.02k|      }
 1333|  12.3k|      ++p;
 1334|  12.3k|    }
 1335|  17.8k|  }
 1336|  3.52k|  if (p != pend) {
  ------------------
  |  Branch (1336:7): [True: 631, False: 2.88k]
  ------------------
 1337|    631|    if (p == pend - 1 && *p == '.') {
  ------------------
  |  Branch (1337:9): [True: 371, False: 260]
  |  Branch (1337:26): [True: 72, False: 299]
  ------------------
 1338|     72|      return ipv4;
 1339|     72|    }
 1340|    559|    return ipv4_fast_fail;
 1341|    631|  }
 1342|  2.88k|  return ipv4;
 1343|  3.52k|}
_ZNK3ada3url15has_credentialsEv:
 7369|  19.2k|[[nodiscard]] ada_really_inline bool url::has_credentials() const noexcept {
 7370|  19.2k|  return !username.empty() || !password.empty();
  ------------------
  |  Branch (7370:10): [True: 2.03k, False: 17.2k]
  |  Branch (7370:31): [True: 268, False: 16.9k]
  ------------------
 7371|  19.2k|}
_ZNK3ada8url_base10is_specialEv:
 7334|   312k|    const noexcept {
 7335|   312k|  return type != ada::scheme::NOT_SPECIAL;
 7336|   312k|}
_ZNK2tl8expectedIN3ada3urlENS1_6errorsEEcvbEv:
 4081|  26.3k|  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEEptEv:
 4050|  18.4k|  TL_EXPECTED_11_CONSTEXPR T* operator->() {
 4051|  18.4k|    TL_ASSERT(has_value());
  ------------------
  |  | 2103|  18.4k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (4051:5): [True: 18.4k, False: 0]
  ------------------
 4052|  18.4k|    return valptr();
 4053|  18.4k|  }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEE6valptrEv:
 3346|  18.4k|  T* valptr() { return std::addressof(this->m_val); }
_ZN2tl6detail21expected_storage_baseIN3ada3urlENS2_6errorsELb0ELb1EED2Ev:
 2678|  46.5k|  ~expected_storage_base() {
 2679|  46.5k|    if (m_has_val) {
  ------------------
  |  Branch (2679:9): [True: 22.4k, False: 24.1k]
  ------------------
 2680|  22.4k|      m_val.~T();
 2681|  22.4k|    }
 2682|  46.5k|  }
_ZNK3ada3url13get_href_sizeEv:
 7627|  23.7k|[[nodiscard]] inline size_t url::get_href_size() const noexcept {
 7628|  23.7k|  size_t size = 0;
 7629|  23.7k|  if (is_special()) {
  ------------------
  |  Branch (7629:7): [True: 14.7k, False: 8.94k]
  ------------------
 7630|  14.7k|    size += ada::scheme::details::is_special_list[type].size() + 1;
 7631|  14.7k|  } else {
 7632|  8.94k|    size += non_special_scheme.size() + 1;
 7633|  8.94k|  }
 7634|  23.7k|  if (host.has_value()) {
  ------------------
  |  Branch (7634:7): [True: 16.2k, False: 7.40k]
  ------------------
 7635|  16.2k|    size += host->size();
 7636|  16.2k|    size += 2;
 7637|  16.2k|    if (has_credentials()) {
  ------------------
  |  Branch (7637:9): [True: 1.56k, False: 14.7k]
  ------------------
 7638|  1.56k|      size += username.size();
 7639|  1.56k|      if (!password.empty()) {
  ------------------
  |  Branch (7639:11): [True: 481, False: 1.08k]
  ------------------
 7640|    481|        size += 1 + password.size();
 7641|    481|      }
 7642|  1.56k|      size += 1;
 7643|  1.56k|    }
 7644|  16.2k|    if (port.has_value()) {
  ------------------
  |  Branch (7644:9): [True: 2.78k, False: 13.5k]
  ------------------
 7645|  2.78k|      size += 1;
 7646|  2.78k|      uint16_t p = *port;
 7647|  2.78k|      size += (p >= 10000)  ? 5
  ------------------
  |  Branch (7647:15): [True: 113, False: 2.66k]
  ------------------
 7648|  2.78k|              : (p >= 1000) ? 4
  ------------------
  |  Branch (7648:17): [True: 116, False: 2.55k]
  ------------------
 7649|  2.66k|              : (p >= 100)  ? 3
  ------------------
  |  Branch (7649:17): [True: 93, False: 2.45k]
  ------------------
 7650|  2.55k|              : (p >= 10)   ? 2
  ------------------
  |  Branch (7650:17): [True: 72, False: 2.38k]
  ------------------
 7651|  2.45k|                            : 1;
 7652|  2.78k|    }
 7653|  16.2k|  } else if (!has_opaque_path && path.starts_with("//")) {
  ------------------
  |  Branch (7653:14): [True: 2.72k, False: 4.68k]
  |  Branch (7653:34): [True: 90, False: 2.63k]
  ------------------
 7654|     90|    size += 2;
 7655|     90|  }
 7656|  23.7k|  size += path.size();
 7657|  23.7k|  if (query.has_value()) {
  ------------------
  |  Branch (7657:7): [True: 1.67k, False: 22.0k]
  ------------------
 7658|  1.67k|    size += 1 + query->size();
 7659|  1.67k|  }
 7660|  23.7k|  if (hash.has_value()) {
  ------------------
  |  Branch (7660:7): [True: 1.25k, False: 22.4k]
  ------------------
 7661|  1.25k|    size += 1 + hash->size();
 7662|  1.25k|  }
 7663|  23.7k|  return size;
 7664|  23.7k|}
_ZN3ada8checkers8is_alphaEc:
 1274|  94.0k|constexpr bool is_alpha(char x) noexcept {
 1275|  94.0k|  return (to_lower(x) >= 'a') && (to_lower(x) <= 'z');
  ------------------
  |  Branch (1275:10): [True: 85.9k, False: 8.07k]
  |  Branch (1275:34): [True: 85.6k, False: 277]
  ------------------
 1276|  94.0k|}
_ZN3ada8checkers8to_lowerEc:
 1272|   180k|constexpr char to_lower(char x) noexcept { return (x | 0x20); }
_ZNR2tl8expectedIN3ada3urlENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 4063|  3.93k|  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
 4064|  3.93k|    TL_ASSERT(has_value());
  ------------------
  |  | 2103|  3.93k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (4064:5): [True: 3.93k, False: 0]
  ------------------
 4065|  3.93k|    return val();
 4066|  3.93k|  }
_ZN2tl8expectedIN3ada3urlENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 3355|  3.93k|  TL_EXPECTED_11_CONSTEXPR U& val() {
 3356|  3.93k|    return this->m_val;
 3357|  3.93k|  }
_ZN3ada3urlaSERKS0_:
 5053|    142|  url& operator=(const url& u) = default;
_ZNK2tl8expectedIN3ada3urlENS1_6errorsEE9has_valueEv:
 4080|  72.8k|  constexpr bool has_value() const noexcept { return this->m_has_val; }
_ZN3ada8checkers26last_label_may_be_a_numberENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1247|  46.4k|constexpr bool last_label_may_be_a_number(std::string_view view) noexcept {
 1248|  46.4k|  if (view.empty()) {
  ------------------
  |  Branch (1248:7): [True: 0, False: 46.4k]
  ------------------
 1249|      0|    return false;
 1250|      0|  }
 1251|  46.4k|  const char* start = view.data();
 1252|  46.4k|  const char* end = start + view.size();
 1253|  46.4k|  if (end[-1] == '.') {
  ------------------
  |  Branch (1253:7): [True: 4.61k, False: 41.8k]
  ------------------
 1254|  4.61k|    --end;
 1255|  4.61k|    if (end == start) {
  ------------------
  |  Branch (1255:9): [True: 2.98k, False: 1.62k]
  ------------------
 1256|  2.98k|      return false;
 1257|  2.98k|    }
 1258|  4.61k|  }
 1259|  43.4k|  if (!is_ipv4_number_char(end[-1])) {
  ------------------
  |  Branch (1259:7): [True: 20.6k, False: 22.8k]
  ------------------
 1260|  20.6k|    return false;
 1261|  20.6k|  }
 1262|  22.8k|  const char* label = end;
 1263|   156k|  while (label != start && is_ipv4_number_char(label[-1])) {
  ------------------
  |  Branch (1263:10): [True: 144k, False: 11.1k]
  |  Branch (1263:28): [True: 133k, False: 11.7k]
  ------------------
 1264|   133k|    --label;
 1265|   133k|  }
 1266|  22.8k|  if (label != start && label[-1] != '.') {
  ------------------
  |  Branch (1266:7): [True: 11.7k, False: 11.1k]
  |  Branch (1266:25): [True: 8.32k, False: 3.43k]
  ------------------
 1267|  8.32k|    return false;
 1268|  8.32k|  }
 1269|  14.5k|  return label != end && is_digit(*label);
  ------------------
  |  Branch (1269:10): [True: 14.5k, False: 0]
  |  Branch (1269:26): [True: 9.24k, False: 5.30k]
  ------------------
 1270|  22.8k|}
_ZN3ada8checkers19is_ipv4_number_charEc:
 1241|   188k|constexpr bool is_ipv4_number_char(char x) noexcept {
 1242|   188k|  const unsigned char c = static_cast<unsigned char>(x);
 1243|   188k|  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
  ------------------
  |  Branch (1243:11): [True: 177k, False: 10.6k]
  |  Branch (1243:23): [True: 65.4k, False: 112k]
  |  Branch (1243:37): [True: 110k, False: 12.9k]
  |  Branch (1243:49): [True: 62.5k, False: 47.5k]
  ------------------
 1244|  60.4k|         (c >= 'A' && c <= 'F') || c == 'x' || c == 'X';
  ------------------
  |  Branch (1244:11): [True: 49.1k, False: 11.3k]
  |  Branch (1244:23): [True: 726, False: 48.4k]
  |  Branch (1244:36): [True: 27.0k, False: 32.7k]
  |  Branch (1244:48): [True: 340, False: 32.3k]
  ------------------
 1245|   188k|}
_ZN3ada8checkers8is_digitEc:
 1239|  46.9k|constexpr bool is_digit(char x) noexcept { return (x >= '0') & (x <= '9'); }
_ZN3ada6scheme16get_special_portENS0_4typeE:
 6848|  7.82k|constexpr uint16_t get_special_port(ada::scheme::type type) noexcept {
 6849|  7.82k|  return details::special_ports[int(type)];
 6850|  7.82k|}
_ZN3ada14url_aggregatoraSERKS0_:
 7838|    142|  url_aggregator& operator=(const url_aggregator& u) = default;
_ZN3ada14url_aggregator10clear_hashEv:
 8916|    123|inline void url_aggregator::clear_hash() {
 8917|    123|  ada_log("url_aggregator::clear_hash");
 8918|    123|  ADA_ASSERT_TRUE(validate());
 8919|    123|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8919:7): [True: 102, False: 21]
  ------------------
 8920|    102|    return;
 8921|    102|  }
 8922|     21|  buffer.resize(components.hash_start);
 8923|     21|  components.hash_start = url_components::omitted;
 8924|       |
 8925|       |#if ADA_DEVELOPMENT_CHECKS
 8926|       |  ADA_ASSERT_EQUAL(get_hash(), "",
 8927|       |                   "hash should have been cleared on buffer=" + buffer +
 8928|       |                       " with " + components.to_string() + "\n" + to_diagram());
 8929|       |#endif
 8930|     21|  ADA_ASSERT_TRUE(validate());
 8931|     21|}
_ZN3ada14url_aggregator20update_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8559|  16.5k|inline void url_aggregator::update_base_pathname(const std::string_view input) {
 8560|  16.5k|  ada_log("url_aggregator::update_base_pathname '", input, "' [", input.size(),
 8561|  16.5k|          " bytes] \n", to_diagram());
 8562|  16.5k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8563|  16.5k|  ADA_ASSERT_TRUE(validate());
 8564|       |
 8565|  16.5k|  const bool begins_with_dashdash = input.starts_with("//");
 8566|  16.5k|  if (!begins_with_dashdash && has_dash_dot()) {
  ------------------
  |  Branch (8566:7): [True: 15.9k, False: 592]
  |  Branch (8566:32): [True: 9, False: 15.9k]
  ------------------
 8567|       |    // We must delete the ./
 8568|      9|    delete_dash_dot();
 8569|      9|  }
 8570|       |
 8571|  16.5k|  if (begins_with_dashdash && !has_opaque_path && !has_authority() &&
  ------------------
  |  Branch (8571:7): [True: 592, False: 15.9k]
  |  Branch (8571:31): [True: 592, False: 0]
  |  Branch (8571:51): [True: 67, False: 525]
  ------------------
 8572|     67|      !has_dash_dot()) {
  ------------------
  |  Branch (8572:7): [True: 61, False: 6]
  ------------------
 8573|       |    // If url's host is null, url does not have an opaque path, url's path's
 8574|       |    // size is greater than 1, then append U+002F (/) followed by U+002E (.) to
 8575|       |    // output.
 8576|     61|    buffer.insert(components.pathname_start, "/.");
 8577|     61|    components.pathname_start += 2;
 8578|     61|    if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8578:9): [True: 0, False: 61]
  ------------------
 8579|      0|      components.search_start += 2;
 8580|      0|    }
 8581|     61|    if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8581:9): [True: 0, False: 61]
  ------------------
 8582|      0|      components.hash_start += 2;
 8583|      0|    }
 8584|     61|  }
 8585|       |
 8586|  16.5k|  uint32_t difference = replace_and_resize(
 8587|  16.5k|      components.pathname_start,
 8588|  16.5k|      components.pathname_start + get_pathname_length(), input);
 8589|  16.5k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8589:7): [True: 0, False: 16.5k]
  ------------------
 8590|      0|    components.search_start += difference;
 8591|      0|  }
 8592|  16.5k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8592:7): [True: 0, False: 16.5k]
  ------------------
 8593|      0|    components.hash_start += difference;
 8594|      0|  }
 8595|  16.5k|  ADA_ASSERT_TRUE(validate());
 8596|  16.5k|}
_ZN3ada14url_aggregator18replace_and_resizeEjjNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8401|  51.0k|    uint32_t start, uint32_t end, std::string_view input) {
 8402|  51.0k|  uint32_t current_length = end - start;
 8403|  51.0k|  uint32_t input_size = uint32_t(input.size());
 8404|  51.0k|  uint32_t new_difference = input_size - current_length;
 8405|       |
 8406|  51.0k|  if (current_length == 0) {
  ------------------
  |  Branch (8406:7): [True: 46.7k, False: 4.25k]
  ------------------
 8407|  46.7k|    buffer.insert(start, input);
 8408|  46.7k|  } else if (input_size == current_length) {
  ------------------
  |  Branch (8408:14): [True: 584, False: 3.67k]
  ------------------
 8409|    584|    if (input_size != 0) {
  ------------------
  |  Branch (8409:9): [True: 584, False: 0]
  ------------------
 8410|    584|      std::memmove(buffer.data() + start, input.data(), input_size);
 8411|    584|    }
 8412|  3.67k|  } else if (input_size < current_length) {
  ------------------
  |  Branch (8412:14): [True: 701, False: 2.97k]
  ------------------
 8413|    701|    buffer.erase(start, current_length - input_size);
 8414|    701|    buffer.replace(start, input_size, input);
 8415|  2.97k|  } else {
 8416|  2.97k|    buffer.replace(start, current_length, input.substr(0, current_length));
 8417|  2.97k|    buffer.insert(start + current_length, input.substr(current_length));
 8418|  2.97k|  }
 8419|       |
 8420|  51.0k|  return new_difference;
 8421|  51.0k|}
_ZNK3ada14url_aggregator19get_pathname_lengthEv:
 8452|  16.5k|url_aggregator::get_pathname_length() const noexcept {
 8453|  16.5k|  ada_log("url_aggregator::get_pathname_length");
 8454|  16.5k|  uint32_t ending_index = uint32_t(buffer.size());
 8455|  16.5k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8455:7): [True: 0, False: 16.5k]
  ------------------
 8456|      0|    ending_index = components.search_start;
 8457|  16.5k|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8457:14): [True: 0, False: 16.5k]
  ------------------
 8458|      0|    ending_index = components.hash_start;
 8459|      0|  }
 8460|  16.5k|  return ending_index - components.pathname_start;
 8461|  16.5k|}
_ZNK3ada14url_aggregator12get_pathnameEv:
 9394|  7.97k|    ada_lifetime_bound {
 9395|  7.97k|  ada_log("url_aggregator::get_pathname pathname_start = ",
 9396|  7.97k|          components.pathname_start, " buffer.size() = ", buffer.size(),
 9397|  7.97k|          " components.search_start = ", components.search_start,
 9398|  7.97k|          " components.hash_start = ", components.hash_start);
 9399|  7.97k|  auto ending_index = uint32_t(buffer.size());
 9400|  7.97k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9400:7): [True: 160, False: 7.81k]
  ------------------
 9401|    160|    ending_index = components.search_start;
 9402|  7.81k|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9402:14): [True: 61, False: 7.75k]
  ------------------
 9403|     61|    ending_index = components.hash_start;
 9404|     61|  }
 9405|  7.97k|  return helpers::substring(buffer, components.pathname_start, ending_index);
 9406|  7.97k|}
_ZN3ada14url_aggregator26update_unencoded_base_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8377|    984|inline void url_aggregator::update_unencoded_base_hash(std::string_view input) {
 8378|    984|  ada_log("url_aggregator::update_unencoded_base_hash ", input, " [",
 8379|    984|          input.size(), " bytes], buffer is '", buffer, "' [", buffer.size(),
 8380|    984|          " bytes] components.hash_start = ", components.hash_start);
 8381|    984|  ADA_ASSERT_TRUE(validate());
 8382|    984|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8383|    984|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8383:7): [True: 11, False: 973]
  ------------------
 8384|     11|    buffer.resize(components.hash_start);
 8385|     11|  }
 8386|    984|  components.hash_start = uint32_t(buffer.size());
 8387|    984|  buffer += "#";
 8388|    984|  bool encoding_required = unicode::percent_encode<true>(
 8389|    984|      input, ada::character_sets::FRAGMENT_PERCENT_ENCODE, buffer);
 8390|       |  // When encoding_required is false, then buffer is left unchanged, and percent
 8391|       |  // encoding was not deemed required.
 8392|    984|  if (!encoding_required) {
  ------------------
  |  Branch (8392:7): [True: 673, False: 311]
  ------------------
 8393|    673|    buffer.append(input);
 8394|    673|  }
 8395|    984|  ada_log("url_aggregator::update_unencoded_base_hash final buffer is '",
 8396|    984|          buffer, "' [", buffer.size(), " bytes]");
 8397|    984|  ADA_ASSERT_TRUE(validate());
 8398|    984|}
_ZN3ada3urlC2Ev:
 5049|  46.5k|  url() = default;
_ZN3ada3url11copy_schemeERKS0_:
 7535|    645|constexpr void url::copy_scheme(const ada::url& u) {
 7536|    645|  non_special_scheme = u.non_special_scheme;
 7537|    645|  type = u.type;
 7538|    645|}
_ZN3ada3url26update_unencoded_base_hashENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7477|    879|inline void url::update_unencoded_base_hash(std::string_view input) {
 7478|       |  // We do the percent encoding
 7479|    879|  hash = unicode::percent_encode(input,
 7480|    879|                                 ada::character_sets::FRAGMENT_PERCENT_ENCODE);
 7481|    879|}
_ZN3ada3url12clear_searchEv:
 7510|    461|constexpr void url::clear_search() { query = std::nullopt; }
_ZN3ada3url18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 7484|  1.13k|                                    const uint8_t query_percent_encode_set[]) {
 7485|  1.13k|  query = ada::unicode::percent_encode(input, query_percent_encode_set);
 7486|  1.13k|}
_ZN3ada3url20update_base_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7475|    130|inline void url::update_base_hostname(std::string_view input) { host = input; }
_ZN3ada3url20update_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 7492|  9.27k|inline void url::update_base_pathname(const std::string_view input) {
 7493|  9.27k|  path = input;
 7494|  9.27k|}
_ZN3ada3url10parse_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
 7667|  1.89k|                                         bool check_trailing_content) noexcept {
 7668|  1.89k|  ada_log("parse_port('", view, "') ", view.size());
 7669|  1.89k|  if (!view.empty() && view[0] == '-') {
  ------------------
  |  Branch (7669:7): [True: 1.28k, False: 616]
  |  Branch (7669:24): [True: 3, False: 1.27k]
  ------------------
 7670|      3|    ada_log("parse_port: view[0] == '0' && view.size() > 1");
 7671|      3|    is_valid = false;
 7672|      3|    return 0;
 7673|      3|  }
 7674|  1.89k|  uint16_t parsed_port{};
 7675|  1.89k|  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
 7676|  1.89k|  if (r.ec == std::errc::result_out_of_range) {
  ------------------
  |  Branch (7676:7): [True: 35, False: 1.85k]
  ------------------
 7677|     35|    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
 7678|     35|    is_valid = false;
 7679|     35|    return 0;
 7680|     35|  }
 7681|  1.85k|  ada_log("parse_port: ", parsed_port);
 7682|  1.85k|  const auto consumed = size_t(r.ptr - view.data());
 7683|  1.85k|  ada_log("parse_port: consumed ", consumed);
 7684|  1.85k|  if (check_trailing_content) {
  ------------------
  |  Branch (7684:7): [True: 1.85k, False: 0]
  ------------------
 7685|  1.85k|    is_valid &=
 7686|  1.85k|        (consumed == view.size() || view[consumed] == '/' ||
  ------------------
  |  Branch (7686:10): [True: 1.19k, False: 664]
  |  Branch (7686:37): [True: 543, False: 121]
  ------------------
 7687|    121|         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
  ------------------
  |  Branch (7687:10): [True: 59, False: 62]
  |  Branch (7687:36): [True: 58, False: 4]
  |  Branch (7687:52): [True: 1, False: 57]
  ------------------
 7688|  1.85k|  }
 7689|  1.85k|  ada_log("parse_port: is_valid = ", is_valid);
 7690|  1.85k|  if (is_valid) {
  ------------------
  |  Branch (7690:7): [True: 1.79k, False: 61]
  ------------------
 7691|       |    // scheme_default_port can return 0, and we should allow 0 as a base port.
 7692|  1.79k|    auto default_port = scheme_default_port();
 7693|  1.79k|    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
  ------------------
  |  Branch (7693:27): [True: 73, False: 1.72k]
  |  Branch (7693:48): [True: 33, False: 40]
  ------------------
 7694|  1.76k|                         (default_port != parsed_port);
  ------------------
  |  Branch (7694:26): [True: 1.75k, False: 7]
  ------------------
 7695|  1.79k|    port = (r.ec == std::errc() && is_port_valid) ? std::optional(parsed_port)
  ------------------
  |  Branch (7695:13): [True: 1.08k, False: 713]
  |  Branch (7695:36): [True: 1.07k, False: 7]
  ------------------
 7696|  1.79k|                                                  : std::nullopt;
 7697|  1.79k|  }
 7698|  1.85k|  return consumed;
 7699|  1.89k|}
_ZNK3ada8url_base19scheme_default_portEv:
 7343|  5.48k|url_base::scheme_default_port() const noexcept {
 7344|  5.48k|  return scheme::get_special_port(type);
 7345|  5.48k|}
_ZNK3ada3url12get_pathnameEv:
 7395|    150|[[nodiscard]] constexpr std::string_view url::get_pathname() const noexcept {
 7396|    150|  return path;
 7397|    150|}
_ZN3ada8checkers23is_windows_drive_letterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1278|  12.1k|constexpr bool is_windows_drive_letter(std::string_view input) noexcept {
 1279|  12.1k|  return input.size() >= 2 &&
  ------------------
  |  Branch (1279:10): [True: 8.35k, False: 3.76k]
  ------------------
 1280|  8.35k|         (is_alpha(input[0]) && ((input[1] == ':') || (input[1] == '|'))) &&
  ------------------
  |  Branch (1280:11): [True: 3.30k, False: 5.05k]
  |  Branch (1280:34): [True: 822, False: 2.47k]
  |  Branch (1280:55): [True: 509, False: 1.96k]
  ------------------
 1281|  1.33k|         ((input.size() == 2) || (input[2] == '/' || input[2] == '\\' ||
  ------------------
  |  Branch (1281:11): [True: 539, False: 792]
  |  Branch (1281:35): [True: 123, False: 669]
  |  Branch (1281:54): [True: 26, False: 643]
  ------------------
 1282|    643|                                  input[2] == '?' || input[2] == '#'));
  ------------------
  |  Branch (1282:35): [True: 7, False: 636]
  |  Branch (1282:54): [True: 0, False: 636]
  ------------------
 1283|  12.1k|}
_ZN3ada8checkers34is_normalized_windows_drive_letterENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 1286|  1.97k|    std::string_view input) noexcept {
 1287|  1.97k|  return input.size() == 2 && (is_alpha(input[0]) && (input[1] == ':'));
  ------------------
  |  Branch (1287:10): [True: 976, False: 1.00k]
  |  Branch (1287:32): [True: 834, False: 142]
  |  Branch (1287:54): [True: 752, False: 82]
  ------------------
 1288|  1.97k|}
_ZN3ada3url20set_protocol_as_fileEv:
 7520|  2.87k|constexpr void url::set_protocol_as_file() { type = ada::scheme::type::FILE; }
_ZN3ada3url14clear_pathnameEv:
 7508|     19|constexpr void url::clear_pathname() { path.clear(); }
_ZN3ada7helpers14leading_zeroesEj:
 1985|  42.1k|inline int leading_zeroes(uint32_t input_num) noexcept {
 1986|       |#if ADA_REGULAR_VISUAL_STUDIO
 1987|       |  unsigned long leading_zero(0);
 1988|       |  unsigned long in(input_num);
 1989|       |  return _BitScanReverse(&leading_zero, in) ? int(31 - leading_zero) : 32;
 1990|       |#else
 1991|  42.1k|  return __builtin_clz(input_num);
 1992|  42.1k|#endif  // ADA_REGULAR_VISUAL_STUDIO
 1993|  42.1k|}
_ZN3ada14url_aggregator7reserveEj:
 9067|  42.1k|constexpr void ada::url_aggregator::reserve(uint32_t capacity) {
 9068|  42.1k|  buffer.reserve(capacity);
 9069|  42.1k|}
_ZN3ada14url_aggregator18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8468|    152|inline void url_aggregator::update_base_search(std::string_view input) {
 8469|    152|  ada_log("url_aggregator::update_base_search ", input);
 8470|    152|  ADA_ASSERT_TRUE(validate());
 8471|    152|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8472|    152|  if (input.empty()) {
  ------------------
  |  Branch (8472:7): [True: 0, False: 152]
  ------------------
 8473|      0|    clear_search();
 8474|      0|    return;
 8475|      0|  }
 8476|       |
 8477|    152|  if (input[0] == '?') {
  ------------------
  |  Branch (8477:7): [True: 152, False: 0]
  ------------------
 8478|    152|    input.remove_prefix(1);
 8479|    152|  }
 8480|       |
 8481|    152|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8481:7): [True: 152, False: 0]
  ------------------
 8482|    152|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8482:9): [True: 152, False: 0]
  ------------------
 8483|    152|      components.search_start = uint32_t(buffer.size());
 8484|    152|      buffer += "?";
 8485|    152|    } else {
 8486|      0|      buffer.resize(components.search_start + 1);
 8487|      0|    }
 8488|       |
 8489|    152|    buffer.append(input);
 8490|    152|  } else if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8490:14): [True: 0, False: 0]
  ------------------
 8491|      0|    const uint32_t difference = replace_and_resize(
 8492|      0|        components.search_start + 1, components.hash_start, input);
 8493|      0|    components.hash_start += difference;
 8494|      0|  } else {
 8495|      0|    components.search_start = components.hash_start;
 8496|      0|    buffer.insert(components.search_start, input.size() + 1, '?');
 8497|      0|    if (!input.empty()) {
  ------------------
  |  Branch (8497:9): [True: 0, False: 0]
  ------------------
 8498|      0|      std::memmove(buffer.data() + components.search_start + 1, input.data(),
 8499|      0|                   input.size());
 8500|      0|    }
 8501|      0|    components.hash_start += uint32_t(input.size() + 1);  // Do not forget `?`
 8502|      0|  }
 8503|       |
 8504|    152|  ADA_ASSERT_TRUE(validate());
 8505|    152|}
_ZN3ada14url_aggregator20append_base_passwordENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8785|  7.87k|inline void url_aggregator::append_base_password(const std::string_view input) {
 8786|  7.87k|  ada_log("url_aggregator::append_base_password ", input, " ", to_string(),
 8787|  7.87k|          "\n", to_diagram());
 8788|  7.87k|  ADA_ASSERT_TRUE(validate());
 8789|  7.87k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8790|       |#if ADA_DEVELOPMENT_CHECKS
 8791|       |  // computing the expected password.
 8792|       |  std::string password_expected = std::string(get_password());
 8793|       |  password_expected.append(input);
 8794|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8795|  7.87k|  add_authority_slashes_if_needed();
 8796|       |
 8797|       |  // If input is empty, do nothing.
 8798|  7.87k|  if (input.empty()) {
  ------------------
  |  Branch (8798:7): [True: 2.17k, False: 5.70k]
  ------------------
 8799|  2.17k|    return;
 8800|  2.17k|  }
 8801|       |
 8802|  5.70k|  uint32_t difference = uint32_t(input.size());
 8803|  5.70k|  if (has_password()) {
  ------------------
  |  Branch (8803:7): [True: 5.13k, False: 576]
  ------------------
 8804|  5.13k|    buffer.insert(components.host_start, input);
 8805|  5.13k|  } else {
 8806|    576|    difference++;  // Increment for ":"
 8807|    576|    buffer.insert(components.username_end, ":");
 8808|    576|    buffer.insert(components.username_end + 1, input);
 8809|    576|  }
 8810|  5.70k|  components.host_start += difference;
 8811|       |
 8812|       |  // The following line is required to add "@" to hostname. When updating
 8813|       |  // password if hostname does not start with "@", it is "append_base_password"s
 8814|       |  // responsibility to set it.
 8815|  5.70k|  if (buffer[components.host_start] != '@') {
  ------------------
  |  Branch (8815:7): [True: 393, False: 5.31k]
  ------------------
 8816|    393|    buffer.insert(components.host_start, "@");
 8817|    393|    difference++;
 8818|    393|  }
 8819|       |
 8820|  5.70k|  components.host_end += difference;
 8821|  5.70k|  components.pathname_start += difference;
 8822|  5.70k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8822:7): [True: 0, False: 5.70k]
  ------------------
 8823|      0|    components.search_start += difference;
 8824|      0|  }
 8825|  5.70k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8825:7): [True: 0, False: 5.70k]
  ------------------
 8826|      0|    components.hash_start += difference;
 8827|      0|  }
 8828|       |#if ADA_DEVELOPMENT_CHECKS
 8829|       |  std::string password_after(get_password());
 8830|       |  ADA_ASSERT_EQUAL(
 8831|       |      password_expected, password_after,
 8832|       |      "append_base_password problem after inserting " + std::string(input));
 8833|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8834|  5.70k|  ADA_ASSERT_TRUE(validate());
 8835|  5.70k|}
_ZN3ada14url_aggregator31add_authority_slashes_if_neededEv:
 9041|  54.8k|inline void ada::url_aggregator::add_authority_slashes_if_needed() {
 9042|  54.8k|  ada_log("url_aggregator::add_authority_slashes_if_needed");
 9043|  54.8k|  ADA_ASSERT_TRUE(validate());
 9044|       |  // Protocol setter will insert `http:` to the URL. It is up to hostname setter
 9045|       |  // to insert
 9046|       |  // `//` initially to the buffer, since it depends on the hostname existence.
 9047|  54.8k|  if (has_authority()) {
  ------------------
  |  Branch (9047:7): [True: 27.7k, False: 27.1k]
  ------------------
 9048|  27.7k|    return;
 9049|  27.7k|  }
 9050|       |  // Performance: the common case is components.protocol_end == buffer.size()
 9051|       |  // Optimization opportunity: in many cases, the "//" is part of the input and
 9052|       |  // the insert could be fused with another insert.
 9053|  27.1k|  buffer.insert(components.protocol_end, "//");
 9054|  27.1k|  components.username_end += 2;
 9055|  27.1k|  components.host_start += 2;
 9056|  27.1k|  components.host_end += 2;
 9057|  27.1k|  components.pathname_start += 2;
 9058|  27.1k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9058:7): [True: 0, False: 27.1k]
  ------------------
 9059|      0|    components.search_start += 2;
 9060|      0|  }
 9061|  27.1k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9061:7): [True: 0, False: 27.1k]
  ------------------
 9062|      0|    components.hash_start += 2;
 9063|      0|  }
 9064|  27.1k|  ADA_ASSERT_TRUE(validate());
 9065|  27.1k|}
_ZN3ada14url_aggregator20append_base_usernameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8669|  12.5k|inline void url_aggregator::append_base_username(const std::string_view input) {
 8670|  12.5k|  ada_log("url_aggregator::append_base_username ", input);
 8671|  12.5k|  ADA_ASSERT_TRUE(validate());
 8672|  12.5k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8673|       |#if ADA_DEVELOPMENT_CHECKS
 8674|       |  // computing the expected password.
 8675|       |  std::string username_expected(get_username());
 8676|       |  username_expected.append(input);
 8677|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8678|  12.5k|  add_authority_slashes_if_needed();
 8679|       |
 8680|       |  // If input is empty, do nothing.
 8681|  12.5k|  if (input.empty()) {
  ------------------
  |  Branch (8681:7): [True: 3.56k, False: 9.00k]
  ------------------
 8682|  3.56k|    return;
 8683|  3.56k|  }
 8684|       |
 8685|  9.00k|  uint32_t difference = uint32_t(input.size());
 8686|  9.00k|  buffer.insert(components.username_end, input);
 8687|  9.00k|  components.username_end += difference;
 8688|  9.00k|  components.host_start += difference;
 8689|       |
 8690|  9.00k|  if (buffer[components.host_start] != '@' &&
  ------------------
  |  Branch (8690:7): [True: 1.21k, False: 7.78k]
  ------------------
 8691|  1.21k|      components.host_start != components.host_end) {
  ------------------
  |  Branch (8691:7): [True: 1.21k, False: 0]
  ------------------
 8692|  1.21k|    buffer.insert(components.host_start, "@");
 8693|  1.21k|    difference++;
 8694|  1.21k|  }
 8695|       |
 8696|  9.00k|  components.host_end += difference;
 8697|  9.00k|  components.pathname_start += difference;
 8698|  9.00k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8698:7): [True: 0, False: 9.00k]
  ------------------
 8699|      0|    components.search_start += difference;
 8700|      0|  }
 8701|  9.00k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8701:7): [True: 0, False: 9.00k]
  ------------------
 8702|      0|    components.hash_start += difference;
 8703|      0|  }
 8704|       |#if ADA_DEVELOPMENT_CHECKS
 8705|       |  std::string username_after(get_username());
 8706|       |  ADA_ASSERT_EQUAL(
 8707|       |      username_expected, username_after,
 8708|       |      "append_base_username problem after inserting " + std::string(input));
 8709|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8710|  9.00k|  ADA_ASSERT_TRUE(validate());
 8711|  9.00k|}
_ZN3ada14url_aggregator21update_base_authorityENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERKNS_14url_componentsE:
 8319|  1.25k|    std::string_view base_buffer, const ada::url_components& base) {
 8320|  1.25k|  std::string_view input = base_buffer.substr(
 8321|  1.25k|      base.protocol_end, base.host_start - base.protocol_end);
 8322|  1.25k|  ada_log("url_aggregator::update_base_authority ", input);
 8323|       |
 8324|  1.25k|  bool input_starts_with_dash = input.starts_with("//");
 8325|  1.25k|  uint32_t diff = components.host_start - components.protocol_end;
 8326|       |
 8327|  1.25k|  buffer.erase(components.protocol_end,
 8328|  1.25k|               components.host_start - components.protocol_end);
 8329|  1.25k|  components.username_end = components.protocol_end;
 8330|       |
 8331|  1.25k|  if (input_starts_with_dash) {
  ------------------
  |  Branch (8331:7): [True: 1.02k, False: 236]
  ------------------
 8332|  1.02k|    input.remove_prefix(2);
 8333|  1.02k|    diff += 2;  // add "//"
 8334|  1.02k|    buffer.insert(components.protocol_end, "//");
 8335|  1.02k|    components.username_end += 2;
 8336|  1.02k|  }
 8337|       |
 8338|  1.25k|  size_t password_delimiter = input.find(':');
 8339|       |
 8340|       |  // Check if input contains both username and password by checking the
 8341|       |  // delimiter: ":" A typical input that contains authority would be "user:pass"
 8342|  1.25k|  if (password_delimiter != std::string_view::npos) {
  ------------------
  |  Branch (8342:7): [True: 36, False: 1.22k]
  ------------------
 8343|       |    // Insert both username and password
 8344|     36|    std::string_view username = input.substr(0, password_delimiter);
 8345|     36|    std::string_view password = input.substr(password_delimiter + 1);
 8346|       |
 8347|     36|    buffer.insert(components.protocol_end + diff, username);
 8348|     36|    diff += uint32_t(username.size());
 8349|     36|    buffer.insert(components.protocol_end + diff, ":");
 8350|     36|    components.username_end = components.protocol_end + diff;
 8351|     36|    buffer.insert(components.protocol_end + diff + 1, password);
 8352|     36|    diff += uint32_t(password.size()) + 1;
 8353|  1.22k|  } else if (!input.empty()) {
  ------------------
  |  Branch (8353:14): [True: 37, False: 1.18k]
  ------------------
 8354|       |    // Insert only username
 8355|     37|    buffer.insert(components.protocol_end + diff, input);
 8356|     37|    components.username_end =
 8357|     37|        components.protocol_end + diff + uint32_t(input.size());
 8358|     37|    diff += uint32_t(input.size());
 8359|     37|  }
 8360|       |
 8361|  1.25k|  components.host_start += diff;
 8362|       |
 8363|  1.25k|  if (buffer.size() > base.host_start && buffer[base.host_start] != '@') {
  ------------------
  |  Branch (8363:7): [True: 0, False: 1.25k]
  |  Branch (8363:42): [True: 0, False: 0]
  ------------------
 8364|      0|    buffer.insert(components.host_start, "@");
 8365|      0|    diff++;
 8366|      0|  }
 8367|  1.25k|  components.host_end += diff;
 8368|  1.25k|  components.pathname_start += diff;
 8369|  1.25k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8369:7): [True: 0, False: 1.25k]
  ------------------
 8370|      0|    components.search_start += diff;
 8371|      0|  }
 8372|  1.25k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8372:7): [True: 0, False: 1.25k]
  ------------------
 8373|      0|    components.hash_start += diff;
 8374|      0|  }
 8375|  1.25k|}
_ZNK3ada14url_aggregator8get_hrefEv:
 9145|  27.4k|    const noexcept ada_lifetime_bound {
 9146|  27.4k|  ada_log("url_aggregator::get_href");
 9147|  27.4k|  return buffer;
 9148|  27.4k|}
_ZNK3ada14url_aggregator14get_componentsEv:
 9027|  1.25k|url_aggregator::get_components() const noexcept {
 9028|  1.25k|  return components;
 9029|  1.25k|}
_ZN3ada14url_aggregator24update_host_to_base_hostENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 9413|  1.74k|void url_aggregator::update_host_to_base_host(const std::string_view input) {
 9414|  1.74k|  ada_log("url_aggregator::update_host_to_base_host ", input);
 9415|  1.74k|  ADA_ASSERT_TRUE(validate());
 9416|  1.74k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 9417|  1.74k|  if (type != ada::scheme::type::FILE) {
  ------------------
  |  Branch (9417:7): [True: 1.25k, False: 488]
  ------------------
 9418|       |    // Let host be the result of host parsing host_view with url is not special.
 9419|  1.25k|    if (input.empty() && !is_special()) {
  ------------------
  |  Branch (9419:9): [True: 256, False: 1.00k]
  |  Branch (9419:26): [True: 256, False: 0]
  ------------------
 9420|    256|      if (has_hostname()) {
  ------------------
  |  Branch (9420:11): [True: 20, False: 236]
  ------------------
 9421|     20|        clear_hostname();
 9422|    236|      } else if (has_dash_dot()) {
  ------------------
  |  Branch (9422:18): [True: 0, False: 236]
  ------------------
 9423|      0|        add_authority_slashes_if_needed();
 9424|      0|        delete_dash_dot();
 9425|      0|      }
 9426|    256|      return;
 9427|    256|    }
 9428|  1.25k|  }
 9429|  1.48k|  update_base_hostname(input);
 9430|  1.48k|  ADA_ASSERT_TRUE(validate());
 9431|  1.48k|  return;
 9432|  1.74k|}
_ZN3ada14url_aggregator14clear_hostnameEv:
 8968|     20|constexpr void url_aggregator::clear_hostname() {
 8969|     20|  ada_log("url_aggregator::clear_hostname");
 8970|     20|  ADA_ASSERT_TRUE(validate());
 8971|     20|  if (!has_authority()) {
  ------------------
  |  Branch (8971:7): [True: 0, False: 20]
  ------------------
 8972|      0|    return;
 8973|      0|  }
 8974|     20|  ADA_ASSERT_TRUE(has_authority());
 8975|       |
 8976|     20|  uint32_t hostname_length = components.host_end - components.host_start;
 8977|     20|  uint32_t start = components.host_start;
 8978|       |
 8979|       |  // If hostname starts with "@", we should not remove that character.
 8980|     20|  if (hostname_length > 0 && buffer[start] == '@') {
  ------------------
  |  Branch (8980:7): [True: 0, False: 20]
  |  Branch (8980:30): [True: 0, False: 0]
  ------------------
 8981|      0|    start++;
 8982|      0|    hostname_length--;
 8983|      0|  }
 8984|     20|  buffer.erase(start, hostname_length);
 8985|     20|  components.host_end = start;
 8986|     20|  components.pathname_start -= hostname_length;
 8987|     20|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8987:7): [True: 0, False: 20]
  ------------------
 8988|      0|    components.search_start -= hostname_length;
 8989|      0|  }
 8990|     20|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8990:7): [True: 0, False: 20]
  ------------------
 8991|      0|    components.hash_start -= hostname_length;
 8992|      0|  }
 8993|       |#if ADA_DEVELOPMENT_CHECKS
 8994|       |  ADA_ASSERT_EQUAL(get_hostname(), "",
 8995|       |                   "hostname should have been cleared on buffer=" + buffer +
 8996|       |                       " with " + components.to_string() + "\n" + to_diagram());
 8997|       |#endif
 8998|     20|  ADA_ASSERT_TRUE(has_authority());
 8999|     20|  ADA_ASSERT_EQUAL(has_empty_hostname(), true,
 9000|     20|                   "hostname should have been cleared on buffer=" + buffer +
 9001|     20|                       " with " + components.to_string() + "\n" + to_diagram());
 9002|     20|  ADA_ASSERT_TRUE(validate());
 9003|     20|}
_ZN3ada14url_aggregator16update_base_portEj:
 8837|  3.39k|inline void url_aggregator::update_base_port(uint32_t input) {
 8838|  3.39k|  ada_log("url_aggregator::update_base_port");
 8839|  3.39k|  ADA_ASSERT_TRUE(validate());
 8840|  3.39k|  if (input == url_components::omitted) {
  ------------------
  |  Branch (8840:7): [True: 1.23k, False: 2.16k]
  ------------------
 8841|  1.23k|    clear_port();
 8842|  1.23k|    return;
 8843|  1.23k|  }
 8844|       |  // calling std::to_string(input.value()) is unfortunate given that the port
 8845|       |  // value is probably already available as a string.
 8846|  2.16k|  std::string value = helpers::concat(":", std::to_string(input));
 8847|  2.16k|  uint32_t difference = uint32_t(value.size());
 8848|       |
 8849|  2.16k|  if (components.port != url_components::omitted) {
  ------------------
  |  Branch (8849:7): [True: 0, False: 2.16k]
  ------------------
 8850|      0|    difference -= components.pathname_start - components.host_end;
 8851|      0|    buffer.erase(components.host_end,
 8852|      0|                 components.pathname_start - components.host_end);
 8853|      0|  }
 8854|       |
 8855|  2.16k|  buffer.insert(components.host_end, value);
 8856|  2.16k|  components.pathname_start += difference;
 8857|  2.16k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8857:7): [True: 0, False: 2.16k]
  ------------------
 8858|      0|    components.search_start += difference;
 8859|      0|  }
 8860|  2.16k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8860:7): [True: 0, False: 2.16k]
  ------------------
 8861|      0|    components.hash_start += difference;
 8862|      0|  }
 8863|  2.16k|  components.port = input;
 8864|  2.16k|  ADA_ASSERT_TRUE(validate());
 8865|  2.16k|}
_ZN3ada7helpers6concatIJPKcNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEEEESA_DpT_:
 1975|  2.16k|std::string concat(Args... args) {
 1976|  2.16k|  std::string answer;
 1977|  2.16k|  inner_concat(answer, args...);
 1978|  2.16k|  return answer;
 1979|  2.16k|}
_ZN3ada7helpers12inner_concatIPKcJNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEEEEvRSA_T_DpT0_:
 1964|  2.16k|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1965|  2.16k|  buffer.append(t);
 1966|  2.16k|  return inner_concat(buffer, args...);
 1967|  2.16k|}
_ZN3ada7helpers12inner_concatINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEJEEEvRS8_T_:
 1956|  2.16k|inline void inner_concat(std::string& buffer, T t) {
 1957|  2.16k|  buffer.append(t);
 1958|  2.16k|}
_ZNK3ada14url_aggregator18retrieve_base_portEv:
 8886|  1.25k|[[nodiscard]] inline uint32_t url_aggregator::retrieve_base_port() const {
 8887|  1.25k|  ada_log("url_aggregator::retrieve_base_port");
 8888|  1.25k|  return components.port;
 8889|  1.25k|}
_ZN3ada14url_aggregator12clear_searchEv:
 8891|  1.13k|inline void url_aggregator::clear_search() {
 8892|  1.13k|  ada_log("url_aggregator::clear_search");
 8893|  1.13k|  ADA_ASSERT_TRUE(validate());
 8894|  1.13k|  if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8894:7): [True: 1.06k, False: 74]
  ------------------
 8895|  1.06k|    return;
 8896|  1.06k|  }
 8897|       |
 8898|     74|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8898:7): [True: 74, False: 0]
  ------------------
 8899|     74|    buffer.resize(components.search_start);
 8900|     74|  } else {
 8901|      0|    buffer.erase(components.search_start,
 8902|      0|                 components.hash_start - components.search_start);
 8903|      0|    components.hash_start = components.search_start;
 8904|      0|  }
 8905|       |
 8906|     74|  components.search_start = url_components::omitted;
 8907|       |
 8908|       |#if ADA_DEVELOPMENT_CHECKS
 8909|       |  ADA_ASSERT_EQUAL(get_search(), "",
 8910|       |                   "search should have been cleared on buffer=" + buffer +
 8911|       |                       " with " + components.to_string() + "\n" + to_diagram());
 8912|       |#endif
 8913|     74|  ADA_ASSERT_TRUE(validate());
 8914|     74|}
_ZN3ada14url_aggregator18update_base_searchENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 8508|  1.13k|    std::string_view input, const uint8_t query_percent_encode_set[]) {
 8509|  1.13k|  ada_log("url_aggregator::update_base_search ", input,
 8510|  1.13k|          " with encoding parameter ", to_string(), "\n", to_diagram());
 8511|  1.13k|  ADA_ASSERT_TRUE(validate());
 8512|  1.13k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8513|       |
 8514|  1.13k|  if (components.hash_start == url_components::omitted) {
  ------------------
  |  Branch (8514:7): [True: 1.13k, False: 0]
  ------------------
 8515|  1.13k|    if (components.search_start == url_components::omitted) {
  ------------------
  |  Branch (8515:9): [True: 1.12k, False: 10]
  ------------------
 8516|  1.12k|      components.search_start = uint32_t(buffer.size());
 8517|  1.12k|      buffer += "?";
 8518|  1.12k|    } else {
 8519|     10|      buffer.resize(components.search_start + 1);
 8520|     10|    }
 8521|       |
 8522|  1.13k|    bool encoding_required =
 8523|  1.13k|        unicode::percent_encode<true>(input, query_percent_encode_set, buffer);
 8524|       |    // When encoding_required is false, then buffer is left unchanged, and
 8525|       |    // percent encoding was not deemed required.
 8526|  1.13k|    if (!encoding_required) {
  ------------------
  |  Branch (8526:9): [True: 712, False: 418]
  ------------------
 8527|    712|      buffer.append(input);
 8528|    712|    }
 8529|  1.13k|  } else {
 8530|      0|    const size_t idx =
 8531|      0|        ada::unicode::percent_encode_index(input, query_percent_encode_set);
 8532|      0|    std::string encoded;
 8533|      0|    std::string_view replacement = input;
 8534|      0|    if (idx != input.size()) {
  ------------------
  |  Branch (8534:9): [True: 0, False: 0]
  ------------------
 8535|      0|      encoded =
 8536|      0|          ada::unicode::percent_encode(input, query_percent_encode_set, idx);
 8537|      0|      replacement = encoded;
 8538|      0|    }
 8539|       |
 8540|      0|    if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8540:9): [True: 0, False: 0]
  ------------------
 8541|      0|      const uint32_t difference = replace_and_resize(
 8542|      0|          components.search_start + 1, components.hash_start, replacement);
 8543|      0|      components.hash_start += difference;
 8544|      0|    } else {
 8545|      0|      components.search_start = components.hash_start;
 8546|      0|      buffer.insert(components.search_start, replacement.size() + 1, '?');
 8547|      0|      if (!replacement.empty()) {
  ------------------
  |  Branch (8547:11): [True: 0, False: 0]
  ------------------
 8548|      0|        std::memmove(buffer.data() + components.search_start + 1,
 8549|      0|                     replacement.data(), replacement.size());
 8550|      0|      }
 8551|      0|      components.hash_start +=
 8552|      0|          uint32_t(replacement.size() + 1);  // Do not forget `?`
 8553|      0|    }
 8554|      0|  }
 8555|       |
 8556|  1.13k|  ADA_ASSERT_TRUE(validate());
 8557|  1.13k|}
_ZN3ada14url_aggregator20update_base_hostnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8423|  34.4k|inline void url_aggregator::update_base_hostname(const std::string_view input) {
 8424|  34.4k|  ada_log("url_aggregator::update_base_hostname ", input, " [", input.size(),
 8425|  34.4k|          " bytes], buffer is '", buffer, "' [", buffer.size(), " bytes]");
 8426|  34.4k|  ADA_ASSERT_TRUE(validate());
 8427|  34.4k|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8428|       |
 8429|       |  // This next line is required for when parsing a URL like `foo://`
 8430|  34.4k|  add_authority_slashes_if_needed();
 8431|       |
 8432|  34.4k|  bool has_credentials = components.protocol_end + 2 < components.host_start;
 8433|  34.4k|  uint32_t new_difference =
 8434|  34.4k|      replace_and_resize(components.host_start, components.host_end, input);
 8435|       |
 8436|  34.4k|  if (has_credentials) {
  ------------------
  |  Branch (8436:7): [True: 1.22k, False: 33.2k]
  ------------------
 8437|  1.22k|    buffer.insert(components.host_start, "@");
 8438|  1.22k|    new_difference++;
 8439|  1.22k|  }
 8440|  34.4k|  components.host_end += new_difference;
 8441|  34.4k|  components.pathname_start += new_difference;
 8442|  34.4k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8442:7): [True: 0, False: 34.4k]
  ------------------
 8443|      0|    components.search_start += new_difference;
 8444|      0|  }
 8445|  34.4k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8445:7): [True: 0, False: 34.4k]
  ------------------
 8446|      0|    components.hash_start += new_difference;
 8447|      0|  }
 8448|  34.4k|  ADA_ASSERT_TRUE(validate());
 8449|  34.4k|}
_ZN3ada14url_aggregator10parse_portENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEb:
 9155|  3.87k|url_aggregator::parse_port(std::string_view view, bool check_trailing_content) {
 9156|  3.87k|  ada_log("url_aggregator::parse_port('", view, "') ", view.size());
 9157|  3.87k|  if (!view.empty() && view[0] == '-') {
  ------------------
  |  Branch (9157:7): [True: 2.62k, False: 1.24k]
  |  Branch (9157:24): [True: 6, False: 2.62k]
  ------------------
 9158|      6|    ada_log("parse_port: view[0] == '0' && view.size() > 1");
 9159|      6|    is_valid = false;
 9160|      6|    return 0;
 9161|      6|  }
 9162|  3.86k|  uint16_t parsed_port{};
 9163|  3.86k|  auto r = std::from_chars(view.data(), view.data() + view.size(), parsed_port);
 9164|  3.86k|  if (r.ec == std::errc::result_out_of_range) {
  ------------------
  |  Branch (9164:7): [True: 63, False: 3.80k]
  ------------------
 9165|     63|    ada_log("parse_port: r.ec == std::errc::result_out_of_range");
 9166|     63|    is_valid = false;
 9167|     63|    return 0;
 9168|     63|  }
 9169|  3.80k|  ada_log("parse_port: ", parsed_port);
 9170|  3.80k|  const size_t consumed = size_t(r.ptr - view.data());
 9171|  3.80k|  ada_log("parse_port: consumed ", consumed);
 9172|  3.80k|  if (check_trailing_content) {
  ------------------
  |  Branch (9172:7): [True: 3.80k, False: 0]
  ------------------
 9173|  3.80k|    is_valid &=
 9174|  3.80k|        (consumed == view.size() || view[consumed] == '/' ||
  ------------------
  |  Branch (9174:10): [True: 2.40k, False: 1.39k]
  |  Branch (9174:37): [True: 1.15k, False: 239]
  ------------------
 9175|    239|         view[consumed] == '?' || (is_special() && view[consumed] == '\\'));
  ------------------
  |  Branch (9175:10): [True: 126, False: 113]
  |  Branch (9175:36): [True: 105, False: 8]
  |  Branch (9175:52): [True: 2, False: 103]
  ------------------
 9176|  3.80k|  }
 9177|  3.80k|  ada_log("parse_port: is_valid = ", is_valid);
 9178|  3.80k|  if (is_valid) {
  ------------------
  |  Branch (9178:7): [True: 3.69k, False: 111]
  ------------------
 9179|  3.69k|    ada_log("parse_port", r.ec == std::errc());
 9180|       |    // scheme_default_port can return 0, and we should allow 0 as a base port.
 9181|  3.69k|    auto default_port = scheme_default_port();
 9182|  3.69k|    bool is_port_valid = (default_port == 0 && parsed_port == 0) ||
  ------------------
  |  Branch (9182:27): [True: 146, False: 3.54k]
  |  Branch (9182:48): [True: 66, False: 80]
  ------------------
 9183|  3.62k|                         (default_port != parsed_port);
  ------------------
  |  Branch (9183:26): [True: 3.61k, False: 16]
  ------------------
 9184|  3.69k|    if (r.ec == std::errc() && is_port_valid) {
  ------------------
  |  Branch (9184:9): [True: 2.15k, False: 1.53k]
  |  Branch (9184:32): [True: 2.14k, False: 16]
  ------------------
 9185|  2.14k|      update_base_port(parsed_port);
 9186|  2.14k|    } else {
 9187|  1.55k|      clear_port();
 9188|  1.55k|    }
 9189|  3.69k|  }
 9190|  3.80k|  return consumed;
 9191|  3.86k|}
_ZN3ada14url_aggregator20append_base_pathnameENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE:
 8598|      4|inline void url_aggregator::append_base_pathname(const std::string_view input) {
 8599|      4|  ada_log("url_aggregator::append_base_pathname ", input, " ", to_string(),
 8600|      4|          "\n", to_diagram());
 8601|      4|  ADA_ASSERT_TRUE(validate());
 8602|      4|  ADA_ASSERT_TRUE(!helpers::overlaps(input, buffer));
 8603|       |#if ADA_DEVELOPMENT_CHECKS
 8604|       |  // computing the expected password.
 8605|       |  std::string path_expected(get_pathname());
 8606|       |  path_expected.append(input);
 8607|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8608|      4|  uint32_t ending_index = uint32_t(buffer.size());
 8609|      4|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8609:7): [True: 0, False: 4]
  ------------------
 8610|      0|    ending_index = components.search_start;
 8611|      4|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8611:14): [True: 0, False: 4]
  ------------------
 8612|      0|    ending_index = components.hash_start;
 8613|      0|  }
 8614|      4|  buffer.insert(ending_index, input);
 8615|       |
 8616|      4|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8616:7): [True: 0, False: 4]
  ------------------
 8617|      0|    components.search_start += uint32_t(input.size());
 8618|      0|  }
 8619|      4|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8619:7): [True: 0, False: 4]
  ------------------
 8620|      0|    components.hash_start += uint32_t(input.size());
 8621|      0|  }
 8622|       |#if ADA_DEVELOPMENT_CHECKS
 8623|       |  std::string path_after = std::string(get_pathname());
 8624|       |  ADA_ASSERT_EQUAL(
 8625|       |      path_expected, path_after,
 8626|       |      "append_base_pathname problem after inserting " + std::string(input));
 8627|       |#endif  // ADA_DEVELOPMENT_CHECKS
 8628|      4|  ADA_ASSERT_TRUE(validate());
 8629|      4|}
_ZN3ada14url_aggregator20set_protocol_as_fileEv:
 9193|  5.74k|constexpr void url_aggregator::set_protocol_as_file() {
 9194|  5.74k|  ada_log("url_aggregator::set_protocol_as_file ");
 9195|  5.74k|  ADA_ASSERT_TRUE(validate());
 9196|  5.74k|  type = ada::scheme::type::FILE;
 9197|       |  // next line could overflow but unsigned arithmetic has well-defined
 9198|       |  // overflows.
 9199|  5.74k|  uint32_t new_difference = 5 - components.protocol_end;
 9200|       |
 9201|  5.74k|  if (buffer.empty()) {
  ------------------
  |  Branch (9201:7): [True: 446, False: 5.29k]
  ------------------
 9202|    446|    buffer.append("file:");
 9203|  5.29k|  } else {
 9204|  5.29k|    buffer.erase(0, components.protocol_end);
 9205|  5.29k|    buffer.insert(0, "file:");
 9206|  5.29k|  }
 9207|  5.74k|  components.protocol_end = 5;
 9208|       |
 9209|       |  // Update the rest of the components.
 9210|  5.74k|  components.username_end += new_difference;
 9211|  5.74k|  components.host_start += new_difference;
 9212|  5.74k|  components.host_end += new_difference;
 9213|  5.74k|  components.pathname_start += new_difference;
 9214|  5.74k|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (9214:7): [True: 0, False: 5.74k]
  ------------------
 9215|      0|    components.search_start += new_difference;
 9216|      0|  }
 9217|  5.74k|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (9217:7): [True: 0, False: 5.74k]
  ------------------
 9218|      0|    components.hash_start += new_difference;
 9219|      0|  }
 9220|  5.74k|  ADA_ASSERT_TRUE(validate());
 9221|  5.74k|}
_ZN3ada14url_aggregator14clear_pathnameEv:
 8933|     38|constexpr void url_aggregator::clear_pathname() {
 8934|     38|  ada_log("url_aggregator::clear_pathname");
 8935|     38|  ADA_ASSERT_TRUE(validate());
 8936|     38|  uint32_t ending_index = uint32_t(buffer.size());
 8937|     38|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8937:7): [True: 0, False: 38]
  ------------------
 8938|      0|    ending_index = components.search_start;
 8939|     38|  } else if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8939:14): [True: 0, False: 38]
  ------------------
 8940|      0|    ending_index = components.hash_start;
 8941|      0|  }
 8942|     38|  uint32_t pathname_length = ending_index - components.pathname_start;
 8943|     38|  buffer.erase(components.pathname_start, pathname_length);
 8944|     38|  uint32_t difference = pathname_length;
 8945|     38|  if (components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (8945:7): [True: 0, False: 38]
  ------------------
 8946|      0|      buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (8946:7): [True: 0, False: 0]
  ------------------
 8947|      0|      buffer[components.host_end + 1] == '.') {
  ------------------
  |  Branch (8947:7): [True: 0, False: 0]
  ------------------
 8948|      0|    components.pathname_start -= 2;
 8949|      0|    buffer.erase(components.host_end, 2);
 8950|      0|    difference += 2;
 8951|      0|  }
 8952|     38|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8952:7): [True: 0, False: 38]
  ------------------
 8953|      0|    components.search_start -= difference;
 8954|      0|  }
 8955|     38|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8955:7): [True: 0, False: 38]
  ------------------
 8956|      0|    components.hash_start -= difference;
 8957|      0|  }
 8958|     38|  ada_log("url_aggregator::clear_pathname completed, running checks...");
 8959|       |#if ADA_DEVELOPMENT_CHECKS
 8960|       |  ADA_ASSERT_EQUAL(get_pathname(), "",
 8961|       |                   "pathname should have been cleared on buffer=" + buffer +
 8962|       |                       " with " + components.to_string() + "\n" + to_diagram());
 8963|       |#endif
 8964|     38|  ADA_ASSERT_TRUE(validate());
 8965|     38|  ada_log("url_aggregator::clear_pathname completed, running checks... ok");
 8966|     38|}
_ZN3ada7unicode20percent_encode_indexENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPKh:
 8278|  1.23k|                                              const uint8_t character_set[]) {
 8279|  1.23k|  const char* data = input.data();
 8280|  1.23k|  const size_t size = input.size();
 8281|       |
 8282|       |  // Process 8 bytes at a time using unrolled loop
 8283|  1.23k|  size_t i = 0;
 8284|  4.40k|  for (; i + 8 <= size; i += 8) {
  ------------------
  |  Branch (8284:10): [True: 3.48k, False: 912]
  ------------------
 8285|  3.48k|    unsigned char chunk[8];
 8286|  3.48k|    std::memcpy(&chunk, data + i,
 8287|  3.48k|                8);  // entices compiler to unconditionally process 8 characters
 8288|       |
 8289|       |    // Check 8 characters at once
 8290|  28.9k|    for (size_t j = 0; j < 8; j++) {
  ------------------
  |  Branch (8290:24): [True: 25.7k, False: 3.17k]
  ------------------
 8291|  25.7k|      if (character_sets::bit_at(character_set, chunk[j])) {
  ------------------
  |  Branch (8291:11): [True: 318, False: 25.4k]
  ------------------
 8292|    318|        return i + j;
 8293|    318|      }
 8294|  25.7k|    }
 8295|  3.48k|  }
 8296|       |
 8297|       |  // Handle remaining bytes
 8298|  2.39k|  for (; i < size; i++) {
  ------------------
  |  Branch (8298:10): [True: 1.69k, False: 706]
  ------------------
 8299|  1.69k|    if (character_sets::bit_at(character_set, data[i])) {
  ------------------
  |  Branch (8299:9): [True: 206, False: 1.48k]
  ------------------
 8300|    206|      return i;
 8301|    206|    }
 8302|  1.69k|  }
 8303|       |
 8304|    706|  return size;
 8305|    912|}
_ZN3ada14url_aggregator10clear_portEv:
 8867|  2.78k|inline void url_aggregator::clear_port() {
 8868|  2.78k|  ada_log("url_aggregator::clear_port");
 8869|  2.78k|  ADA_ASSERT_TRUE(validate());
 8870|  2.78k|  if (components.port == url_components::omitted) {
  ------------------
  |  Branch (8870:7): [True: 2.78k, False: 0]
  ------------------
 8871|  2.78k|    return;
 8872|  2.78k|  }
 8873|      0|  uint32_t length = components.pathname_start - components.host_end;
 8874|      0|  buffer.erase(components.host_end, length);
 8875|      0|  components.pathname_start -= length;
 8876|      0|  if (components.search_start != url_components::omitted) {
  ------------------
  |  Branch (8876:7): [True: 0, False: 0]
  ------------------
 8877|      0|    components.search_start -= length;
 8878|      0|  }
 8879|      0|  if (components.hash_start != url_components::omitted) {
  ------------------
  |  Branch (8879:7): [True: 0, False: 0]
  ------------------
 8880|      0|    components.hash_start -= length;
 8881|      0|  }
 8882|      0|  components.port = url_components::omitted;
 8883|      0|  ADA_ASSERT_TRUE(validate());
 8884|      0|}
_ZNK3ada14url_aggregator13has_authorityEv:
 9032|  55.7k|    const noexcept {
 9033|  55.7k|  ada_log("url_aggregator::has_authority");
 9034|       |  // Performance: instead of doing this potentially expensive check, we could
 9035|       |  // have a boolean in the struct.
 9036|  55.7k|  return components.protocol_end + 2 <= components.host_start &&
  ------------------
  |  Branch (9036:10): [True: 28.2k, False: 27.4k]
  ------------------
 9037|  28.2k|         buffer[components.protocol_end] == '/' &&
  ------------------
  |  Branch (9037:10): [True: 28.2k, False: 0]
  ------------------
 9038|  28.2k|         buffer[components.protocol_end + 1] == '/';
  ------------------
  |  Branch (9038:10): [True: 28.2k, False: 0]
  ------------------
 9039|  55.7k|}
_ZNK3ada14url_aggregator12has_dash_dotEv:
 9112|  16.2k|[[nodiscard]] constexpr bool url_aggregator::has_dash_dot() const noexcept {
 9113|       |  // If url's host is null, url does not have an opaque path, url's path's size
 9114|       |  // is greater than 1, and url's path[0] is the empty string, then append
 9115|       |  // U+002F (/) followed by U+002E (.) to output.
 9116|  16.2k|  ada_log("url_aggregator::has_dash_dot");
 9117|       |#if ADA_DEVELOPMENT_CHECKS
 9118|       |  // If pathname_start and host_end are exactly two characters apart, then we
 9119|       |  // either have a one-digit port such as http://test.com:5?param=1 or else we
 9120|       |  // have a /.: sequence such as "non-spec:/.//". We test that this is the case.
 9121|       |  if (components.pathname_start == components.host_end + 2) {
 9122|       |    ADA_ASSERT_TRUE((buffer[components.host_end] == '/' &&
 9123|       |                     buffer[components.host_end + 1] == '.') ||
 9124|       |                    (buffer[components.host_end] == ':' &&
 9125|       |                     checkers::is_digit(buffer[components.host_end + 1])));
 9126|       |  }
 9127|       |  if (components.pathname_start == components.host_end + 2 &&
 9128|       |      buffer[components.host_end] == '/' &&
 9129|       |      buffer[components.host_end + 1] == '.') {
 9130|       |    ADA_ASSERT_TRUE(components.pathname_start + 1 < buffer.size());
 9131|       |    ADA_ASSERT_TRUE(buffer[components.pathname_start] == '/');
 9132|       |    ADA_ASSERT_TRUE(buffer[components.pathname_start + 1] == '/');
 9133|       |  }
 9134|       |#endif
 9135|       |  // Performance: it should be uncommon for components.pathname_start ==
 9136|       |  // components.host_end + 2 to be true. So we put this check first in the
 9137|       |  // sequence. Most times, we do not have an opaque path. Checking for '/.' is
 9138|       |  // more expensive, but should be uncommon.
 9139|  16.2k|  return components.pathname_start == components.host_end + 2 &&
  ------------------
  |  Branch (9139:10): [True: 932, False: 15.3k]
  ------------------
 9140|    932|         !has_opaque_path && buffer[components.host_end] == '/' &&
  ------------------
  |  Branch (9140:10): [True: 932, False: 0]
  |  Branch (9140:30): [True: 15, False: 917]
  ------------------
 9141|     15|         buffer[components.host_end + 1] == '.';
  ------------------
  |  Branch (9141:10): [True: 15, False: 0]
  ------------------
 9142|  16.2k|}
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEcvbEv:
 4081|  42.6k|  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEptEv:
 4050|  26.2k|  TL_EXPECTED_11_CONSTEXPR T* operator->() {
 4051|  26.2k|    TL_ASSERT(has_value());
  ------------------
  |  | 2103|  26.2k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (4051:5): [True: 26.2k, False: 0]
  ------------------
 4052|  26.2k|    return valptr();
 4053|  26.2k|  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE6valptrEv:
 3346|  26.2k|  T* valptr() { return std::addressof(this->m_val); }
_ZNR2tl8expectedIN3ada14url_aggregatorENS1_6errorsEEdeIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 4063|  3.93k|  TL_EXPECTED_11_CONSTEXPR U& operator*() & {
 4064|  3.93k|    TL_ASSERT(has_value());
  ------------------
  |  | 2103|  3.93k|#define TL_ASSERT(x) assert(x)
  ------------------
  |  Branch (4064:5): [True: 3.93k, False: 0]
  ------------------
 4065|  3.93k|    return val();
 4066|  3.93k|  }
_ZN2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE3valIS2_TnPNSt3__19enable_ifIXntsr3std7is_voidIT_EE5valueEvE4typeELPv0EEERS8_v:
 3355|  3.93k|  TL_EXPECTED_11_CONSTEXPR U& val() {
 3356|  3.93k|    return this->m_val;
 3357|  3.93k|  }
_ZNK2tl8expectedIN3ada14url_aggregatorENS1_6errorsEE9has_valueEv:
 4080|  84.5k|  constexpr bool has_value() const noexcept { return this->m_has_val; }
_ZN2tl6detail21expected_storage_baseIN3ada14url_aggregatorENS2_6errorsELb0ELb1EED2Ev:
 2678|  46.5k|  ~expected_storage_base() {
 2679|  46.5k|    if (m_has_val) {
  ------------------
  |  Branch (2679:9): [True: 22.4k, False: 24.1k]
  ------------------
 2680|  22.4k|      m_val.~T();
 2681|  22.4k|    }
 2682|  46.5k|  }
_ZN3ada7helpers9substringENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEmm:
 1897|  28.1k|                                                       size_t pos2) {
 1898|       |#if ADA_DEVELOPMENT_CHECKS
 1899|       |  if (pos2 < pos1) {
 1900|       |    std::cerr << "Negative-length substring: [" << pos1 << " to " << pos2 << ")"
 1901|       |              << std::endl;
 1902|       |    abort();
 1903|       |  }
 1904|       |#endif
 1905|  28.1k|  return input.substr(pos1, pos2 - pos1);
 1906|  28.1k|}
_ZNK3ada14url_aggregator10is_at_pathEv:
 8464|  4.64k|    const noexcept {
 8465|  4.64k|  return buffer.size() == components.pathname_start;
 8466|  4.64k|}
_ZNK3ada14url_aggregator12has_hostnameEv:
 9101|    256|constexpr bool url_aggregator::has_hostname() const noexcept {
 9102|    256|  return has_authority();
 9103|    256|}
_ZNK3ada14url_aggregator12has_passwordEv:
 9081|  5.70k|constexpr bool url_aggregator::has_password() const noexcept {
 9082|  5.70k|  ada_log("url_aggregator::has_password");
 9083|       |  // This function does not care about the length of the password
 9084|  5.70k|  return components.host_start > components.username_end &&
  ------------------
  |  Branch (9084:10): [True: 5.13k, False: 576]
  ------------------
 9085|  5.13k|         buffer[components.username_end] == ':';
  ------------------
  |  Branch (9085:10): [True: 5.13k, False: 0]
  ------------------
 9086|  5.70k|}
_ZNK3ada3url8get_hrefEv:
 7540|  18.4k|[[nodiscard]] ada_really_inline std::string url::get_href() const {
 7541|  18.4k|  if (is_special() && host.has_value() && username.empty() &&
  ------------------
  |  Branch (7541:7): [True: 15.1k, False: 3.29k]
  |  Branch (7541:23): [True: 15.1k, False: 0]
  |  Branch (7541:43): [True: 14.6k, False: 561]
  ------------------
 7542|  14.6k|      password.empty() && !port.has_value()) [[likely]] {
  ------------------
  |  Branch (7542:7): [True: 14.5k, False: 73]
  |  Branch (7542:27): [True: 12.9k, False: 1.58k]
  ------------------
 7543|  12.9k|    const std::string_view scheme = ada::scheme::details::is_special_list[type];
 7544|  12.9k|    const size_t host_size = host->size();
 7545|  12.9k|    const size_t path_size = path.size();
 7546|  12.9k|    const size_t query_size = query.has_value() ? query->size() : 0;
  ------------------
  |  Branch (7546:31): [True: 730, False: 12.2k]
  ------------------
 7547|  12.9k|    const size_t hash_size = hash.has_value() ? hash->size() : 0;
  ------------------
  |  Branch (7547:30): [True: 552, False: 12.4k]
  ------------------
 7548|  12.9k|    const size_t total = scheme.size() + 3 + host_size + path_size +
 7549|  12.9k|                         (query.has_value() ? query_size + 1 : 0) +
  ------------------
  |  Branch (7549:27): [True: 730, False: 12.2k]
  ------------------
 7550|  12.9k|                         (hash.has_value() ? hash_size + 1 : 0);
  ------------------
  |  Branch (7550:27): [True: 552, False: 12.4k]
  ------------------
 7551|  12.9k|    std::string output(total, '\0');
 7552|  12.9k|    char* p = output.data();
 7553|  12.9k|    std::memcpy(p, scheme.data(), scheme.size());
 7554|  12.9k|    p += scheme.size();
 7555|  12.9k|    p[0] = ':';
 7556|  12.9k|    p[1] = '/';
 7557|  12.9k|    p[2] = '/';
 7558|  12.9k|    p += 3;
 7559|       |    // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
 7560|  12.9k|    std::memcpy(p, host->data(), host_size);
 7561|  12.9k|    p += host_size;
 7562|  12.9k|    std::memcpy(p, path.data(), path_size);
 7563|  12.9k|    p += path_size;
 7564|  12.9k|    if (query.has_value()) {
  ------------------
  |  Branch (7564:9): [True: 730, False: 12.2k]
  ------------------
 7565|    730|      *p++ = '?';
 7566|       |      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
 7567|    730|      std::memcpy(p, query->data(), query_size);
 7568|    730|      p += query_size;
 7569|    730|    }
 7570|  12.9k|    if (hash.has_value()) {
  ------------------
  |  Branch (7570:9): [True: 552, False: 12.4k]
  ------------------
 7571|    552|      *p++ = '#';
 7572|       |      // NOLINTNEXTLINE(bugprone-not-null-terminated-result)
 7573|    552|      std::memcpy(p, hash->data(), hash_size);
 7574|    552|    }
 7575|  12.9k|    return output;
 7576|  12.9k|  }
 7577|       |
 7578|  5.51k|  std::string output;
 7579|  5.51k|  output.reserve(get_href_size());
 7580|       |
 7581|  5.51k|  if (is_special()) {
  ------------------
  |  Branch (7581:7): [True: 2.21k, False: 3.29k]
  ------------------
 7582|  2.21k|    output.append(ada::scheme::details::is_special_list[type]);
 7583|  2.21k|    output += ':';
 7584|  3.29k|  } else {
 7585|  3.29k|    output.append(non_special_scheme);
 7586|  3.29k|    output += ':';
 7587|  3.29k|  }
 7588|       |
 7589|  5.51k|  if (host.has_value()) {
  ------------------
  |  Branch (7589:7): [True: 2.94k, False: 2.56k]
  ------------------
 7590|  2.94k|    output += '/';
 7591|  2.94k|    output += '/';
 7592|  2.94k|    if (has_credentials()) {
  ------------------
  |  Branch (7592:9): [True: 737, False: 2.21k]
  ------------------
 7593|    737|      output.append(username);
 7594|    737|      if (!password.empty()) {
  ------------------
  |  Branch (7594:11): [True: 220, False: 517]
  ------------------
 7595|    220|        output += ':';
 7596|    220|        output.append(password);
 7597|    220|      }
 7598|    737|      output += '@';
 7599|    737|    }
 7600|  2.94k|    output.append(*host);
 7601|  2.94k|    if (port.has_value()) {
  ------------------
  |  Branch (7601:9): [True: 1.69k, False: 1.25k]
  ------------------
 7602|  1.69k|      output += ':';
 7603|  1.69k|      char port_buf[5];
 7604|  1.69k|      auto [ptr, ec] = std::to_chars(port_buf, port_buf + 5, *port);
 7605|  1.69k|      (void)ec;
 7606|  1.69k|      output.append(port_buf, static_cast<size_t>(ptr - port_buf));
 7607|  1.69k|    }
 7608|  2.94k|  } else if (!has_opaque_path && path.starts_with("//")) {
  ------------------
  |  Branch (7608:14): [True: 1.28k, False: 1.28k]
  |  Branch (7608:34): [True: 38, False: 1.24k]
  ------------------
 7609|       |    // If url's host is null, url does not have an opaque path, url's path's
 7610|       |    // size is greater than 1, and url's path[0] is the empty string, then
 7611|       |    // append U+002F (/) followed by U+002E (.) to output.
 7612|     38|    output += '/';
 7613|     38|    output += '.';
 7614|     38|  }
 7615|  5.51k|  output.append(path);
 7616|  5.51k|  if (query.has_value()) {
  ------------------
  |  Branch (7616:7): [True: 564, False: 4.94k]
  ------------------
 7617|    564|    output += '?';
 7618|    564|    output.append(*query);
 7619|    564|  }
 7620|  5.51k|  if (hash.has_value()) {
  ------------------
  |  Branch (7620:7): [True: 465, False: 5.04k]
  ------------------
 7621|    465|    output += '#';
 7622|    465|    output.append(*hash);
 7623|    465|  }
 7624|  5.51k|  return output;
 7625|  18.4k|}
_ZNK3ada14url_aggregator10has_searchEv:
 9010|  1.62k|[[nodiscard]] constexpr bool url_aggregator::has_search() const noexcept {
 9011|  1.62k|  ada_log("url_aggregator::has_search");
 9012|  1.62k|  return components.search_start != url_components::omitted;
 9013|  1.62k|}
_ZN3ada7helpers6concatIJNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEPKcEEENS2_12basic_stringIcS5_NS2_9allocatorIcEEEEDpT_:
 1975|  21.8k|std::string concat(Args... args) {
 1976|  21.8k|  std::string answer;
 1977|  21.8k|  inner_concat(answer, args...);
 1978|  21.8k|  return answer;
 1979|  21.8k|}
_ZN3ada7helpers12inner_concatINSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEJPKcEEEvRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEET_DpT0_:
 1964|  21.8k|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1965|  21.8k|  buffer.append(t);
 1966|  21.8k|  return inner_concat(buffer, args...);
 1967|  21.8k|}
_ZN3ada7helpers12inner_concatIPKcJEEEvRNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEET_:
 1956|  21.8k|inline void inner_concat(std::string& buffer, T t) {
 1957|  21.8k|  buffer.append(t);
 1958|  21.8k|}
_ZN3ada3url10set_schemeEONSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE:
 7522|  10.9k|inline void url::set_scheme(std::string&& new_scheme) noexcept {
 7523|  10.9k|  type = ada::scheme::get_scheme_type(new_scheme);
 7524|       |  // We only move the 'scheme' if it is non-special.
 7525|  10.9k|  if (!is_special()) {
  ------------------
  |  Branch (7525:7): [True: 5.54k, False: 5.39k]
  ------------------
 7526|  5.54k|    non_special_scheme = std::move(new_scheme);
 7527|  5.54k|  }
 7528|  10.9k|}
_ZN3ada7helpers6concatIJPKcNSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEEEENS4_12basic_stringIcS7_NS4_9allocatorIcEEEEDpT_:
 1975|      4|std::string concat(Args... args) {
 1976|      4|  std::string answer;
 1977|      4|  inner_concat(answer, args...);
 1978|      4|  return answer;
 1979|      4|}
_ZN3ada7helpers12inner_concatIPKcJNSt3__117basic_string_viewIcNS4_11char_traitsIcEEEEEEEvRNS4_12basic_stringIcS7_NS4_9allocatorIcEEEET_DpT0_:
 1964|      4|inline void inner_concat(std::string& buffer, T t, Args... args) {
 1965|      4|  buffer.append(t);
 1966|      4|  return inner_concat(buffer, args...);
 1967|      4|}
_ZN3ada7helpers12inner_concatINSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEJEEEvRNS2_12basic_stringIcS5_NS2_9allocatorIcEEEET_:
 1956|      4|inline void inner_concat(std::string& buffer, T t) {
 1957|      4|  buffer.append(t);
 1958|      4|}

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

