_Z29get_supported_implementationsv:
   26|  2.48k|get_supported_implementations() {
   27|  2.48k|  static const auto impl = []() -> auto {
   28|  2.48k|    std::vector<const simdutf::implementation*> ret;
   29|  2.48k|    for (auto e : simdutf::get_available_implementations()) {
   30|  2.48k|      std::cerr << "implementation " << e->name() << " is available? "
   31|  2.48k|                << e->supported_by_runtime_system() << '\n';
   32|  2.48k|      if (e->supported_by_runtime_system()) {
   33|  2.48k|        ret.push_back(e);
   34|  2.48k|      }
   35|  2.48k|    }
   36|  2.48k|    return ret;
   37|  2.48k|  }();
   38|  2.48k|  return {impl.data(), impl.size()};
   39|  2.48k|}
_ZZ29get_supported_implementationsvENKUlvE_clEv:
   27|      1|  static const auto impl = []() -> auto {
   28|      1|    std::vector<const simdutf::implementation*> ret;
   29|      4|    for (auto e : simdutf::get_available_implementations()) {
  ------------------
  |  Branch (29:17): [True: 4, False: 1]
  ------------------
   30|      4|      std::cerr << "implementation " << e->name() << " is available? "
   31|      4|                << e->supported_by_runtime_system() << '\n';
   32|      4|      if (e->supported_by_runtime_system()) {
  ------------------
  |  Branch (32:11): [True: 3, False: 1]
  ------------------
   33|      3|        ret.push_back(e);
   34|      3|      }
   35|      4|    }
   36|      1|    return ret;
   37|      1|  }();
_ZneRKN7simdutf6resultES2_:
   42|  4.96k|inline bool operator!=(const simdutf::result& a, const simdutf::result& b) {
   43|  4.96k|  return a.count != b.count || a.error != b.error;
  ------------------
  |  Branch (43:10): [True: 0, False: 4.96k]
  |  Branch (43:32): [True: 0, False: 4.96k]
  ------------------
   44|  4.96k|}

LLVMFuzzerTestOneInput:
  211|  2.48k|extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  212|       |  // First 4 bytes: action selector + alignment padding.
  213|  2.48k|  if (size < 4) {
  ------------------
  |  Branch (213:7): [True: 2, False: 2.48k]
  ------------------
  214|      2|    return 0;
  215|      2|  }
  216|  2.48k|  constexpr auto Ncases = 2u;
  217|  2.48k|  constexpr auto actionmask = std::bit_ceil(Ncases) - 1;
  218|  2.48k|  const auto action = data[0] & actionmask;
  219|       |
  220|       |  // Advance by 4 so the remaining data is aligned to char16_t.
  221|  2.48k|  data += 4;
  222|  2.48k|  size -= 4;
  223|       |
  224|  2.48k|  const std::span<const char16_t> u16data{
  225|  2.48k|      reinterpret_cast<const char16_t*>(data), size / sizeof(char16_t)};
  226|       |
  227|  2.48k|  switch (action) {
  ------------------
  |  Branch (227:11): [True: 2.48k, False: 0]
  ------------------
  228|  1.27k|  case 0:
  ------------------
  |  Branch (228:3): [True: 1.27k, False: 1.20k]
  ------------------
  229|  1.27k|    test_utf16le_with_replacement(u16data);
  230|  1.27k|    break;
  231|  1.20k|  case 1:
  ------------------
  |  Branch (231:3): [True: 1.20k, False: 1.27k]
  ------------------
  232|  1.20k|    test_utf16be_with_replacement(u16data);
  233|  1.20k|    break;
  234|  2.48k|  }
  235|       |
  236|  2.48k|  return 0;
  237|  2.48k|}
with_replacement.cpp:_ZL29test_utf16le_with_replacementNSt3__14spanIKDsLm18446744073709551615EEE:
   27|  1.27k|static void test_utf16le_with_replacement(std::span<const char16_t> input) {
   28|  1.27k|  const auto implementations = get_supported_implementations();
   29|  1.27k|  if (implementations.empty()) {
  ------------------
  |  Branch (29:7): [True: 0, False: 1.27k]
  ------------------
   30|      0|    return;
   31|      0|  }
   32|       |
   33|       |  // Step 1: Collect length predictions from all implementations and check
   34|       |  // agreement.
   35|  1.27k|  std::vector<simdutf::result> len_results;
   36|  1.27k|  len_results.reserve(implementations.size());
   37|  3.83k|  for (auto impl : implementations) {
  ------------------
  |  Branch (37:18): [True: 3.83k, False: 1.27k]
  ------------------
   38|  3.83k|    len_results.push_back(impl->utf8_length_from_utf16le_with_replacement(
   39|  3.83k|        input.data(), input.size()));
   40|  3.83k|  }
   41|  1.27k|  {
   42|  1.27k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
   43|  1.27k|    if (std::ranges::adjacent_find(len_results, neq) != len_results.end()) {
  ------------------
  |  Branch (43:9): [True: 0, False: 1.27k]
  ------------------
   44|      0|      std::cerr << "utf8_length_from_utf16le_with_replacement: implementations "
   45|      0|                   "disagree\n";
   46|      0|      for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (46:31): [True: 0, False: 0]
  ------------------
   47|      0|        std::cerr << "  " << implementations[i]->name()
   48|      0|                  << ": count=" << len_results[i].count
   49|      0|                  << " error=" << len_results[i].error << "\n";
   50|      0|      }
   51|      0|      std::abort();
   52|      0|    }
   53|  1.27k|  }
   54|       |
   55|  1.27k|  const std::size_t expected_len = len_results[0].count;
   56|       |  // error == SUCCESS means no surrogates encountered; SURROGATE means at least
   57|       |  // one.
   58|  1.27k|  const bool has_surrogates = (len_results[0].error != simdutf::SUCCESS);
   59|       |
   60|       |  // Step 2: Run conversion across all implementations and verify written ==
   61|       |  // expected_len.
   62|  1.27k|  std::vector<std::vector<char>> outputs;
   63|  1.27k|  outputs.reserve(implementations.size());
   64|  3.83k|  for (auto impl : implementations) {
  ------------------
  |  Branch (64:18): [True: 3.83k, False: 1.27k]
  ------------------
   65|  3.83k|    std::vector<char> out(expected_len);
   66|  3.83k|    const auto written = impl->convert_utf16le_to_utf8_with_replacement(
   67|  3.83k|        input.data(), input.size(), out.data());
   68|  3.83k|    if (written != expected_len) {
  ------------------
  |  Branch (68:9): [True: 0, False: 3.83k]
  ------------------
   69|      0|      std::cerr << "convert_utf16le_to_utf8_with_replacement:" << " written="
   70|      0|                << written << " but length predicted=" << expected_len
   71|      0|                << " impl=" << impl->name() << "\n";
   72|      0|      std::abort();
   73|      0|    }
   74|  3.83k|    outputs.push_back(std::move(out));
   75|  3.83k|  }
   76|       |
   77|       |  // Step 3: All implementations must agree on the output bytes.
   78|  1.27k|  {
   79|  1.27k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
   80|  1.27k|    if (std::ranges::adjacent_find(outputs, neq) != outputs.end()) {
  ------------------
  |  Branch (80:9): [True: 0, False: 1.27k]
  ------------------
   81|      0|      std::cerr << "convert_utf16le_to_utf8_with_replacement: outputs differ "
   82|      0|                   "between implementations\n";
   83|      0|      for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (83:31): [True: 0, False: 0]
  ------------------
   84|      0|        std::cerr << "  " << implementations[i]->name()
   85|      0|                  << ": hash=" << FNV1A_hash::as_str(outputs[i]) << "\n";
   86|      0|      }
   87|      0|      std::abort();
   88|      0|    }
   89|  1.27k|  }
   90|       |
   91|       |  // Step 4: Output must always be valid UTF-8.
   92|  5.10k|  for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (92:27): [True: 3.83k, False: 1.27k]
  ------------------
   93|  3.83k|    if (!implementations[i]->validate_utf8(outputs[i].data(),
  ------------------
  |  Branch (93:9): [True: 0, False: 3.83k]
  ------------------
   94|  3.83k|                                           outputs[i].size())) {
   95|      0|      std::cerr << "convert_utf16le_to_utf8_with_replacement: output is not "
   96|      0|                   "valid UTF-8"
   97|      0|                << " impl=" << implementations[i]->name() << "\n";
   98|      0|      std::abort();
   99|      0|    }
  100|  3.83k|  }
  101|       |
  102|       |  // Step 5: When no surrogates were found, match the regular (non-replacement)
  103|       |  // length.
  104|  1.27k|  if (!has_surrogates) {
  ------------------
  |  Branch (104:7): [True: 452, False: 825]
  ------------------
  105|  1.80k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (105:29): [True: 1.35k, False: 452]
  ------------------
  106|  1.35k|      auto impl = implementations[i];
  107|  1.35k|      const auto regular_len =
  108|  1.35k|          impl->utf8_length_from_utf16le(input.data(), input.size());
  109|  1.35k|      if (regular_len != expected_len) {
  ------------------
  |  Branch (109:11): [True: 0, False: 1.35k]
  ------------------
  110|      0|        std::cerr
  111|      0|            << "utf16le_with_replacement: no surrogates but length mismatch:"
  112|      0|            << " with_replacement=" << expected_len
  113|      0|            << " regular=" << regular_len << " impl=" << impl->name() << "\n";
  114|      0|        std::abort();
  115|      0|      }
  116|  1.35k|    }
  117|    452|  }
  118|  1.27k|}
with_replacement.cpp:_ZZL29test_utf16le_with_replacementNSt3__14spanIKDsLm18446744073709551615EEEENK3$_0clIN7simdutf6resultES6_EEDaRKT_RKT0_:
   42|  2.55k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
with_replacement.cpp:_ZZL29test_utf16le_with_replacementNSt3__14spanIKDsLm18446744073709551615EEEENK3$_1clINS_6vectorIcNS_9allocatorIcEEEES8_EEDaRKT_RKT0_:
   79|  2.55k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
with_replacement.cpp:_ZL29test_utf16be_with_replacementNSt3__14spanIKDsLm18446744073709551615EEE:
  120|  1.20k|static void test_utf16be_with_replacement(std::span<const char16_t> input) {
  121|  1.20k|  const auto implementations = get_supported_implementations();
  122|  1.20k|  if (implementations.empty()) {
  ------------------
  |  Branch (122:7): [True: 0, False: 1.20k]
  ------------------
  123|      0|    return;
  124|      0|  }
  125|       |
  126|       |  // Step 1: Collect length predictions from all implementations and check
  127|       |  // agreement.
  128|  1.20k|  std::vector<simdutf::result> len_results;
  129|  1.20k|  len_results.reserve(implementations.size());
  130|  3.61k|  for (auto impl : implementations) {
  ------------------
  |  Branch (130:18): [True: 3.61k, False: 1.20k]
  ------------------
  131|  3.61k|    len_results.push_back(impl->utf8_length_from_utf16be_with_replacement(
  132|  3.61k|        input.data(), input.size()));
  133|  3.61k|  }
  134|  1.20k|  {
  135|  1.20k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  136|  1.20k|    if (std::ranges::adjacent_find(len_results, neq) != len_results.end()) {
  ------------------
  |  Branch (136:9): [True: 0, False: 1.20k]
  ------------------
  137|      0|      std::cerr << "utf8_length_from_utf16be_with_replacement: implementations "
  138|      0|                   "disagree\n";
  139|      0|      for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (139:31): [True: 0, False: 0]
  ------------------
  140|      0|        std::cerr << "  " << implementations[i]->name()
  141|      0|                  << ": count=" << len_results[i].count
  142|      0|                  << " error=" << len_results[i].error << "\n";
  143|      0|      }
  144|      0|      std::abort();
  145|      0|    }
  146|  1.20k|  }
  147|       |
  148|  1.20k|  const std::size_t expected_len = len_results[0].count;
  149|  1.20k|  const bool has_surrogates = (len_results[0].error != simdutf::SUCCESS);
  150|       |
  151|       |  // Step 2: Run conversion across all implementations and verify written ==
  152|       |  // expected_len.
  153|  1.20k|  std::vector<std::vector<char>> outputs;
  154|  1.20k|  outputs.reserve(implementations.size());
  155|  3.61k|  for (auto impl : implementations) {
  ------------------
  |  Branch (155:18): [True: 3.61k, False: 1.20k]
  ------------------
  156|  3.61k|    std::vector<char> out(expected_len);
  157|  3.61k|    const auto written = impl->convert_utf16be_to_utf8_with_replacement(
  158|  3.61k|        input.data(), input.size(), out.data());
  159|  3.61k|    if (written != expected_len) {
  ------------------
  |  Branch (159:9): [True: 0, False: 3.61k]
  ------------------
  160|      0|      std::cerr << "convert_utf16be_to_utf8_with_replacement:" << " written="
  161|      0|                << written << " but length predicted=" << expected_len
  162|      0|                << " impl=" << impl->name() << "\n";
  163|      0|      std::abort();
  164|      0|    }
  165|  3.61k|    outputs.push_back(std::move(out));
  166|  3.61k|  }
  167|       |
  168|       |  // Step 3: All implementations must agree on the output bytes.
  169|  1.20k|  {
  170|  1.20k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  171|  1.20k|    if (std::ranges::adjacent_find(outputs, neq) != outputs.end()) {
  ------------------
  |  Branch (171:9): [True: 0, False: 1.20k]
  ------------------
  172|      0|      std::cerr << "convert_utf16be_to_utf8_with_replacement: outputs differ "
  173|      0|                   "between implementations\n";
  174|      0|      for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (174:31): [True: 0, False: 0]
  ------------------
  175|      0|        std::cerr << "  " << implementations[i]->name()
  176|      0|                  << ": hash=" << FNV1A_hash::as_str(outputs[i]) << "\n";
  177|      0|      }
  178|      0|      std::abort();
  179|      0|    }
  180|  1.20k|  }
  181|       |
  182|       |  // Step 4: Output must always be valid UTF-8.
  183|  4.82k|  for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (183:27): [True: 3.61k, False: 1.20k]
  ------------------
  184|  3.61k|    if (!implementations[i]->validate_utf8(outputs[i].data(),
  ------------------
  |  Branch (184:9): [True: 0, False: 3.61k]
  ------------------
  185|  3.61k|                                           outputs[i].size())) {
  186|      0|      std::cerr << "convert_utf16be_to_utf8_with_replacement: output is not "
  187|      0|                   "valid UTF-8"
  188|      0|                << " impl=" << implementations[i]->name() << "\n";
  189|      0|      std::abort();
  190|      0|    }
  191|  3.61k|  }
  192|       |
  193|       |  // Step 5: When no surrogates were found, match the regular (non-replacement)
  194|       |  // length.
  195|  1.20k|  if (!has_surrogates) {
  ------------------
  |  Branch (195:7): [True: 402, False: 803]
  ------------------
  196|  1.60k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (196:29): [True: 1.20k, False: 402]
  ------------------
  197|  1.20k|      auto impl = implementations[i];
  198|  1.20k|      const auto regular_len =
  199|  1.20k|          impl->utf8_length_from_utf16be(input.data(), input.size());
  200|  1.20k|      if (regular_len != expected_len) {
  ------------------
  |  Branch (200:11): [True: 0, False: 1.20k]
  ------------------
  201|      0|        std::cerr
  202|      0|            << "utf16be_with_replacement: no surrogates but length mismatch:"
  203|      0|            << " with_replacement=" << expected_len
  204|      0|            << " regular=" << regular_len << " impl=" << impl->name() << "\n";
  205|      0|        std::abort();
  206|      0|      }
  207|  1.20k|    }
  208|    402|  }
  209|  1.20k|}
with_replacement.cpp:_ZZL29test_utf16be_with_replacementNSt3__14spanIKDsLm18446744073709551615EEEENK3$_0clIN7simdutf6resultES6_EEDaRKT_RKT0_:
  135|  2.41k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
with_replacement.cpp:_ZZL29test_utf16be_with_replacementNSt3__14spanIKDsLm18446744073709551615EEEENK3$_1clINS_6vectorIcNS_9allocatorIcEEEES8_EEDaRKT_RKT0_:
  170|  2.41k|    auto neq = [](const auto& a, const auto& b) { return a != b; };

_ZN7simdutf6resultC2ENS_10error_codeEm:
   87|  9.18M|      : error{err}, count{pos} {}
_ZN7simdutf11full_resultC2ENS_10error_codeEmm:
  111|  9.19M|      : error{err}, input_count{pos_in}, output_count{pos_out} {}

_ZNK7simdutf14implementation4nameEv:
 5562|      4|  virtual std::string_view name() const noexcept { return _name; }
_ZNK7simdutf14implementation25required_instruction_setsEv:
 5613|      8|  virtual uint32_t required_instruction_sets() const {
 5614|      8|    return _required_instruction_sets;
 5615|      8|  }
_ZN7simdutf14implementationC2EPKcS2_j:
 7514|      4|      : _name(name), _description(description),
 7515|      4|        _required_instruction_sets(required_instruction_sets) {}
_ZN7simdutf8internal29available_implementation_listC2Ev:
 7546|      1|  simdutf_really_inline available_implementation_list() {}

simdutf.cpp:_ZN7simdutf8internalL30detect_supported_architecturesEv:
  301|      8|static inline uint32_t detect_supported_architectures() {
  302|      8|  uint32_t eax;
  303|      8|  uint32_t ebx = 0;
  304|      8|  uint32_t ecx = 0;
  305|      8|  uint32_t edx = 0;
  306|      8|  uint32_t host_isa = 0x0;
  307|       |
  308|       |  // EBX for EAX=0x1
  309|      8|  eax = 0x1;
  310|      8|  cpuid(&eax, &ebx, &ecx, &edx);
  311|       |
  312|      8|  if (ecx & cpuid_bit::sse42) {
  ------------------
  |  Branch (312:7): [True: 8, False: 0]
  ------------------
  313|      8|    host_isa |= instruction_set::SSE42;
  314|      8|  }
  315|       |
  316|      8|  if (ecx & cpuid_bit::pclmulqdq) {
  ------------------
  |  Branch (316:7): [True: 8, False: 0]
  ------------------
  317|      8|    host_isa |= instruction_set::PCLMULQDQ;
  318|      8|  }
  319|       |
  320|      8|  if ((ecx & cpuid_bit::osxsave) != cpuid_bit::osxsave) {
  ------------------
  |  Branch (320:7): [True: 0, False: 8]
  ------------------
  321|      0|    return host_isa;
  322|      0|  }
  323|       |
  324|       |  // xgetbv for checking if the OS saves registers
  325|      8|  uint64_t xcr0 = xgetbv();
  326|       |
  327|      8|  if ((xcr0 & cpuid_bit::xcr0_bit::avx256_saved) == 0) {
  ------------------
  |  Branch (327:7): [True: 0, False: 8]
  ------------------
  328|      0|    return host_isa;
  329|      0|  }
  330|       |  // ECX for EAX=0x7
  331|      8|  eax = 0x7;
  332|      8|  ecx = 0x0; // Sub-leaf = 0
  333|      8|  cpuid(&eax, &ebx, &ecx, &edx);
  334|      8|  if (ebx & cpuid_bit::ebx::avx2) {
  ------------------
  |  Branch (334:7): [True: 8, False: 0]
  ------------------
  335|      8|    host_isa |= instruction_set::AVX2;
  336|      8|  }
  337|      8|  if (ebx & cpuid_bit::ebx::bmi1) {
  ------------------
  |  Branch (337:7): [True: 8, False: 0]
  ------------------
  338|      8|    host_isa |= instruction_set::BMI1;
  339|      8|  }
  340|      8|  if (ebx & cpuid_bit::ebx::bmi2) {
  ------------------
  |  Branch (340:7): [True: 8, False: 0]
  ------------------
  341|      8|    host_isa |= instruction_set::BMI2;
  342|      8|  }
  343|      8|  if (!((xcr0 & cpuid_bit::xcr0_bit::avx512_saved) ==
  ------------------
  |  Branch (343:7): [True: 8, False: 0]
  ------------------
  344|      8|        cpuid_bit::xcr0_bit::avx512_saved)) {
  345|      8|    return host_isa;
  346|      8|  }
  347|      0|  if (ebx & cpuid_bit::ebx::avx512f) {
  ------------------
  |  Branch (347:7): [True: 0, False: 0]
  ------------------
  348|      0|    host_isa |= instruction_set::AVX512F;
  349|      0|  }
  350|      0|  if (ebx & cpuid_bit::ebx::avx512bw) {
  ------------------
  |  Branch (350:7): [True: 0, False: 0]
  ------------------
  351|      0|    host_isa |= instruction_set::AVX512BW;
  352|      0|  }
  353|      0|  if (ebx & cpuid_bit::ebx::avx512cd) {
  ------------------
  |  Branch (353:7): [True: 0, False: 0]
  ------------------
  354|      0|    host_isa |= instruction_set::AVX512CD;
  355|      0|  }
  356|      0|  if (ebx & cpuid_bit::ebx::avx512dq) {
  ------------------
  |  Branch (356:7): [True: 0, False: 0]
  ------------------
  357|      0|    host_isa |= instruction_set::AVX512DQ;
  358|      0|  }
  359|      0|  if (ebx & cpuid_bit::ebx::avx512vl) {
  ------------------
  |  Branch (359:7): [True: 0, False: 0]
  ------------------
  360|      0|    host_isa |= instruction_set::AVX512VL;
  361|      0|  }
  362|      0|  if (ecx & cpuid_bit::ecx::avx512vbmi2) {
  ------------------
  |  Branch (362:7): [True: 0, False: 0]
  ------------------
  363|      0|    host_isa |= instruction_set::AVX512VBMI2;
  364|      0|  }
  365|      0|  if (ecx & cpuid_bit::ecx::avx512vpopcnt) {
  ------------------
  |  Branch (365:7): [True: 0, False: 0]
  ------------------
  366|      0|    host_isa |= instruction_set::AVX512VPOPCNTDQ;
  367|      0|  }
  368|      0|  return host_isa;
  369|      8|}
simdutf.cpp:_ZN7simdutf8internalL5cpuidEPjS1_S1_S1_:
  267|     16|                         uint32_t *edx) {
  268|       |  #if defined(_MSC_VER)
  269|       |  int cpu_info[4];
  270|       |  __cpuidex(cpu_info, *eax, *ecx);
  271|       |  *eax = cpu_info[0];
  272|       |  *ebx = cpu_info[1];
  273|       |  *ecx = cpu_info[2];
  274|       |  *edx = cpu_info[3];
  275|       |  #elif (defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)) ||         \
  276|       |      defined(__FILC__)
  277|       |  uint32_t level = *eax;
  278|       |  __get_cpuid(level, eax, ebx, ecx, edx);
  279|       |  #else
  280|     16|  uint32_t a = *eax, b, c = *ecx, d;
  281|     16|  asm volatile("cpuid\n\t" : "+a"(a), "=b"(b), "+c"(c), "=d"(d));
  282|     16|  *eax = a;
  283|     16|  *ebx = b;
  284|     16|  *ecx = c;
  285|     16|  *edx = d;
  286|     16|  #endif
  287|     16|}
simdutf.cpp:_ZN7simdutf8internalL6xgetbvEv:
  289|      8|static inline uint64_t xgetbv() {
  290|       |  #if defined(_MSC_VER)
  291|       |  return _xgetbv(0);
  292|       |  #elif defined(__FILC__)
  293|       |  return zxgetbv();
  294|       |  #else
  295|      8|  uint32_t xcr0_lo, xcr0_hi;
  296|      8|  asm volatile("xgetbv\n\t" : "=a"(xcr0_lo), "=d"(xcr0_hi) : "c"(0));
  297|      8|  return xcr0_lo | ((uint64_t)xcr0_hi << 32);
  298|      8|  #endif
  299|      8|}

_ZN7simdutf6scalar14u16_swap_bytesEt:
    8|   126M|u16_swap_bytes(const uint16_t word) {
    9|   126M|  return uint16_t((word >> 8) | (word << 8));
   10|   126M|}
_ZN7simdutf6scalar5utf1614swap_if_neededILNS_10endiannessE0EEEtt:
   27|  98.5M|template <endianness big_endian> constexpr uint16_t swap_if_needed(uint16_t c) {
   28|  98.5M|  return !match_system(big_endian) ? scalar::u16_swap_bytes(c) : c;
  ------------------
  |  Branch (28:10): [Folded, False: 98.5M]
  ------------------
   29|  98.5M|}
_ZN7simdutf6scalar5utf1614swap_if_neededILNS_10endiannessE1EEEtt:
   27|  75.3M|template <endianness big_endian> constexpr uint16_t swap_if_needed(uint16_t c) {
   28|  75.3M|  return !match_system(big_endian) ? scalar::u16_swap_bytes(c) : c;
  ------------------
  |  Branch (28:10): [True: 75.3M, Folded]
  ------------------
   29|  75.3M|}

_ZN7simdutf6scalar5utf1617is_high_surrogateILNS_10endiannessE0EEEbDs:
  137|  32.7M|template <endianness big_endian> constexpr bool is_high_surrogate(char16_t c) {
  138|  32.7M|  c = scalar::utf16::swap_if_needed<big_endian>(c);
  139|  32.7M|  return (0xd800 <= c && c <= 0xdbff);
  ------------------
  |  Branch (139:11): [True: 3.01M, False: 29.7M]
  |  Branch (139:26): [True: 992k, False: 2.01M]
  ------------------
  140|  32.7M|}
_ZN7simdutf6scalar5utf1616is_low_surrogateILNS_10endiannessE0EEEbDs:
  142|  38.3M|template <endianness big_endian> constexpr bool is_low_surrogate(char16_t c) {
  143|  38.3M|  c = scalar::utf16::swap_if_needed<big_endian>(c);
  144|  38.3M|  return (0xdc00 <= c && c <= 0xdfff);
  ------------------
  |  Branch (144:11): [True: 2.14M, False: 36.1M]
  |  Branch (144:26): [True: 1.54M, False: 597k]
  ------------------
  145|  38.3M|}
_ZN7simdutf6scalar5utf1617is_high_surrogateILNS_10endiannessE1EEEbDs:
  137|  26.0M|template <endianness big_endian> constexpr bool is_high_surrogate(char16_t c) {
  138|  26.0M|  c = scalar::utf16::swap_if_needed<big_endian>(c);
  139|  26.0M|  return (0xd800 <= c && c <= 0xdbff);
  ------------------
  |  Branch (139:11): [True: 2.96M, False: 23.1M]
  |  Branch (139:26): [True: 1.23M, False: 1.72M]
  ------------------
  140|  26.0M|}
_ZN7simdutf6scalar5utf1616is_low_surrogateILNS_10endiannessE1EEEbDs:
  142|  30.3M|template <endianness big_endian> constexpr bool is_low_surrogate(char16_t c) {
  143|  30.3M|  c = scalar::utf16::swap_if_needed<big_endian>(c);
  144|  30.3M|  return (0xdc00 <= c && c <= 0xdfff);
  ------------------
  |  Branch (144:11): [True: 1.94M, False: 28.3M]
  |  Branch (144:26): [True: 1.02M, False: 920k]
  ------------------
  145|  30.3M|}
_ZN7simdutf6scalar5utf1622utf8_length_from_utf16ILNS_10endiannessE0EEEmPKDsm:
   91|  1.35k|                                                  size_t len) {
   92|       |  // We are not BOM aware.
   93|  1.35k|  size_t counter{0};
   94|  16.2M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (94:22): [True: 16.2M, False: 1.35k]
  ------------------
   95|  16.2M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
   96|  16.2M|    counter++; // ASCII
   97|  16.2M|    counter += static_cast<size_t>(
   98|  16.2M|        word >
   99|  16.2M|        0x7F); // non-ASCII is at least 2 bytes, surrogates are 2*2 == 4 bytes
  100|  16.2M|    counter += static_cast<size_t>((word > 0x7FF && word <= 0xD7FF) ||
  ------------------
  |  Branch (100:37): [True: 7.63M, False: 8.58M]
  |  Branch (100:53): [True: 7.56M, False: 73.4k]
  ------------------
  101|  8.66M|                                   (word >= 0xE000)); // three-byte
  ------------------
  |  Branch (101:36): [True: 73.4k, False: 8.58M]
  ------------------
  102|  16.2M|  }
  103|  1.35k|  return counter;
  104|  1.35k|}
_ZN7simdutf6scalar5utf1622utf8_length_from_utf16ILNS_10endiannessE1EEEmPKDsm:
   91|  1.20k|                                                  size_t len) {
   92|       |  // We are not BOM aware.
   93|  1.20k|  size_t counter{0};
   94|  8.34M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (94:22): [True: 8.34M, False: 1.20k]
  ------------------
   95|  8.34M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
   96|  8.34M|    counter++; // ASCII
   97|  8.34M|    counter += static_cast<size_t>(
   98|  8.34M|        word >
   99|  8.34M|        0x7F); // non-ASCII is at least 2 bytes, surrogates are 2*2 == 4 bytes
  100|  8.34M|    counter += static_cast<size_t>((word > 0x7FF && word <= 0xD7FF) ||
  ------------------
  |  Branch (100:37): [True: 3.97M, False: 4.37M]
  |  Branch (100:53): [True: 3.93M, False: 38.6k]
  ------------------
  101|  4.41M|                                   (word >= 0xE000)); // three-byte
  ------------------
  |  Branch (101:36): [True: 38.6k, False: 4.37M]
  ------------------
  102|  8.34M|  }
  103|  1.20k|  return counter;
  104|  1.20k|}
_ZN7simdutf6scalar5utf1639utf8_length_from_utf16_with_replacementILNS_10endiannessE0EEENS_6resultEPKDsm:
  153|  3.83k|utf8_length_from_utf16_with_replacement(const char16_t *p, size_t len) {
  154|  3.83k|  bool any_surrogates = false;
  155|       |  // We are not BOM aware.
  156|  3.83k|  size_t counter{0};
  157|  32.7M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (157:22): [True: 32.7M, False: 3.83k]
  ------------------
  158|  32.7M|    if (is_high_surrogate<big_endian>(p[i])) {
  ------------------
  |  Branch (158:9): [True: 991k, False: 31.7M]
  ------------------
  159|   991k|      any_surrogates = true;
  160|       |      // surrogate pair
  161|   991k|      if (i + 1 < len && is_low_surrogate<big_endian>(p[i + 1])) {
  ------------------
  |  Branch (161:11): [True: 991k, False: 471]
  |  Branch (161:26): [True: 31.8k, False: 959k]
  ------------------
  162|  31.8k|        counter += 4;
  163|  31.8k|        i++; // skip low surrogate
  164|   960k|      } else {
  165|   960k|        counter += 3; // unpaired high surrogate replaced by U+FFFD
  166|   960k|      }
  167|   991k|      continue;
  168|  31.7M|    } else if (is_low_surrogate<big_endian>(p[i])) {
  ------------------
  |  Branch (168:16): [True: 1.50M, False: 30.2M]
  ------------------
  169|  1.50M|      any_surrogates = true;
  170|  1.50M|      counter += 3; // unpaired low surrogate replaced by U+FFFD
  171|  1.50M|      continue;
  172|  1.50M|    }
  173|  30.2M|    char16_t word = !match_system(big_endian) ? u16_swap_bytes(p[i]) : p[i];
  ------------------
  |  Branch (173:21): [Folded, False: 30.2M]
  ------------------
  174|  30.2M|    counter++; // at least 1 byte
  175|  30.2M|    counter +=
  176|  30.2M|        static_cast<size_t>(word > 0x7F); // non-ASCII is at least 2 bytes
  177|  30.2M|    counter += static_cast<size_t>(word > 0x7FF); // three-byte
  178|  30.2M|  }
  179|  3.83k|  return {any_surrogates ? error_code::SURROGATE : error_code::SUCCESS,
  ------------------
  |  Branch (179:11): [True: 1.86k, False: 1.96k]
  ------------------
  180|  3.83k|          counter};
  181|  3.83k|}
_ZN7simdutf6scalar5utf1639utf8_length_from_utf16_with_replacementILNS_10endiannessE1EEENS_6resultEPKDsm:
  153|  3.61k|utf8_length_from_utf16_with_replacement(const char16_t *p, size_t len) {
  154|  3.61k|  bool any_surrogates = false;
  155|       |  // We are not BOM aware.
  156|  3.61k|  size_t counter{0};
  157|  26.0M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (157:22): [True: 26.0M, False: 3.61k]
  ------------------
  158|  26.0M|    if (is_high_surrogate<big_endian>(p[i])) {
  ------------------
  |  Branch (158:9): [True: 1.23M, False: 24.8M]
  ------------------
  159|  1.23M|      any_surrogates = true;
  160|       |      // surrogate pair
  161|  1.23M|      if (i + 1 < len && is_low_surrogate<big_endian>(p[i + 1])) {
  ------------------
  |  Branch (161:11): [True: 1.23M, False: 492]
  |  Branch (161:26): [True: 64.8k, False: 1.17M]
  ------------------
  162|  64.8k|        counter += 4;
  163|  64.8k|        i++; // skip low surrogate
  164|  1.17M|      } else {
  165|  1.17M|        counter += 3; // unpaired high surrogate replaced by U+FFFD
  166|  1.17M|      }
  167|  1.23M|      continue;
  168|  24.8M|    } else if (is_low_surrogate<big_endian>(p[i])) {
  ------------------
  |  Branch (168:16): [True: 948k, False: 23.8M]
  ------------------
  169|   948k|      any_surrogates = true;
  170|   948k|      counter += 3; // unpaired low surrogate replaced by U+FFFD
  171|   948k|      continue;
  172|   948k|    }
  173|  23.8M|    char16_t word = !match_system(big_endian) ? u16_swap_bytes(p[i]) : p[i];
  ------------------
  |  Branch (173:21): [True: 23.8M, Folded]
  ------------------
  174|  23.8M|    counter++; // at least 1 byte
  175|  23.8M|    counter +=
  176|  23.8M|        static_cast<size_t>(word > 0x7F); // non-ASCII is at least 2 bytes
  177|  23.8M|    counter += static_cast<size_t>(word > 0x7FF); // three-byte
  178|  23.8M|  }
  179|  3.61k|  return {any_surrogates ? error_code::SURROGATE : error_code::SUCCESS,
  ------------------
  |  Branch (179:11): [True: 1.77k, False: 1.84k]
  ------------------
  180|  3.61k|          counter};
  181|  3.61k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf819convert_with_errorsILNS_10endiannessE0ELb0EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT1_Esr7simdutf6detailE26index_assignable_from_charIT2_EEENS_11full_resultES8_mS9_m:
  101|  8.41k|                                                    size_t utf8_len = 0) {
  102|  8.41k|  if (check_output && utf8_len == 0) {
  ------------------
  |  Branch (102:7): [Folded, False: 8.41k]
  |  Branch (102:23): [True: 0, False: 0]
  ------------------
  103|      0|    return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, 0, 0);
  104|      0|  }
  105|       |
  106|  8.41k|  size_t pos = 0;
  107|  8.41k|  auto start = utf8_output;
  108|  8.41k|  auto end = utf8_output + utf8_len;
  109|       |
  110|  35.3k|  while (pos < len) {
  ------------------
  |  Branch (110:10): [True: 33.4k, False: 1.87k]
  ------------------
  111|       |#if SIMDUTF_CPLUSPLUS23
  112|       |    if !consteval
  113|       |#endif
  114|  33.4k|    {
  115|       |      // try to convert the next block of 8 bytes
  116|  33.4k|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (116:11): [True: 27.1k, False: 6.33k]
  ------------------
  117|       |                            // they are ascii
  118|  27.1k|        uint64_t v;
  119|  27.1k|        ::memcpy(&v, data + pos, sizeof(uint64_t));
  120|       |        if constexpr (!match_system(big_endian))
  121|       |          v = (v >> 8) | (v << (64 - 8));
  122|  27.1k|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (122:13): [True: 1.40k, False: 25.7k]
  ------------------
  123|  1.40k|          size_t final_pos = pos + 4;
  124|  7.00k|          while (pos < final_pos) {
  ------------------
  |  Branch (124:18): [True: 5.60k, False: 1.40k]
  ------------------
  125|  5.60k|            if (check_output && size_t(end - utf8_output) < 1) {
  ------------------
  |  Branch (125:17): [Folded, False: 5.60k]
  |  Branch (125:33): [True: 0, False: 0]
  ------------------
  126|      0|              return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  127|      0|                                 utf8_output - start);
  128|      0|            }
  129|  5.60k|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (129:30): [Folded, False: 5.60k]
  ------------------
  130|  5.60k|                                 ? char(u16_swap_bytes(data[pos]))
  131|  5.60k|                                 : char(data[pos]);
  132|  5.60k|            pos++;
  133|  5.60k|          }
  134|  1.40k|          continue;
  135|  1.40k|        }
  136|  27.1k|      }
  137|  33.4k|    }
  138|       |
  139|  32.0k|    uint16_t word =
  140|  32.0k|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (140:9): [Folded, False: 32.0k]
  ------------------
  141|  32.0k|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (141:9): [True: 3.90k, False: 28.1k]
  ------------------
  142|       |      // will generate one UTF-8 bytes
  143|  3.90k|      if (check_output && size_t(end - utf8_output) < 1) {
  ------------------
  |  Branch (143:11): [Folded, False: 3.90k]
  |  Branch (143:27): [True: 0, False: 0]
  ------------------
  144|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  145|      0|                           utf8_output - start);
  146|      0|      }
  147|  3.90k|      *utf8_output++ = char(word);
  148|  3.90k|      pos++;
  149|  28.1k|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (149:16): [True: 2.68k, False: 25.4k]
  ------------------
  150|       |      // will generate two UTF-8 bytes
  151|       |      // we have 0b110XXXXX 0b10XXXXXX
  152|  2.68k|      if (check_output && size_t(end - utf8_output) < 2) {
  ------------------
  |  Branch (152:11): [Folded, False: 2.68k]
  |  Branch (152:27): [True: 0, False: 0]
  ------------------
  153|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  154|      0|                           utf8_output - start);
  155|      0|      }
  156|  2.68k|      *utf8_output++ = char((word >> 6) | 0b11000000);
  157|  2.68k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  158|  2.68k|      pos++;
  159|       |
  160|  25.4k|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (160:16): [True: 18.2k, False: 7.24k]
  ------------------
  161|       |      // will generate three UTF-8 bytes
  162|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
  163|  18.2k|      if (check_output && size_t(end - utf8_output) < 3) {
  ------------------
  |  Branch (163:11): [Folded, False: 18.2k]
  |  Branch (163:27): [True: 0, False: 0]
  ------------------
  164|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  165|      0|                           utf8_output - start);
  166|      0|      }
  167|  18.2k|      *utf8_output++ = char((word >> 12) | 0b11100000);
  168|  18.2k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  169|  18.2k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  170|  18.2k|      pos++;
  171|  18.2k|    } else {
  172|       |
  173|  7.24k|      if (check_output && size_t(end - utf8_output) < 4) {
  ------------------
  |  Branch (173:11): [Folded, False: 7.24k]
  |  Branch (173:27): [True: 0, False: 0]
  ------------------
  174|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  175|      0|                           utf8_output - start);
  176|      0|      }
  177|       |      // must be a surrogate pair
  178|  7.24k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (178:11): [True: 672, False: 6.57k]
  ------------------
  179|    672|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  180|    672|      }
  181|  6.57k|      uint16_t diff = uint16_t(word - 0xD800);
  182|  6.57k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (182:11): [True: 2.33k, False: 4.23k]
  ------------------
  183|  2.33k|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  184|  2.33k|      }
  185|  4.23k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (185:28): [Folded, False: 4.23k]
  ------------------
  186|  4.23k|                               ? u16_swap_bytes(data[pos + 1])
  187|  4.23k|                               : data[pos + 1];
  188|  4.23k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
  189|  4.23k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (189:11): [True: 3.52k, False: 711]
  ------------------
  190|  3.52k|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  191|  3.52k|      }
  192|    711|      uint32_t value = (diff << 10) + diff2 + 0x10000;
  193|       |      // will generate four UTF-8 bytes
  194|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
  195|    711|      *utf8_output++ = char((value >> 18) | 0b11110000);
  196|    711|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  197|    711|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  198|    711|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
  199|    711|      pos += 2;
  200|    711|    }
  201|  32.0k|  }
  202|  1.87k|  return full_result(error_code::SUCCESS, pos, utf8_output - start);
  203|  8.41k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf819convert_with_errorsILNS_10endiannessE1ELb0EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT1_Esr7simdutf6detailE26index_assignable_from_charIT2_EEENS_11full_resultES8_mS9_m:
  101|  8.77k|                                                    size_t utf8_len = 0) {
  102|  8.77k|  if (check_output && utf8_len == 0) {
  ------------------
  |  Branch (102:7): [Folded, False: 8.77k]
  |  Branch (102:23): [True: 0, False: 0]
  ------------------
  103|      0|    return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, 0, 0);
  104|      0|  }
  105|       |
  106|  8.77k|  size_t pos = 0;
  107|  8.77k|  auto start = utf8_output;
  108|  8.77k|  auto end = utf8_output + utf8_len;
  109|       |
  110|  33.7k|  while (pos < len) {
  ------------------
  |  Branch (110:10): [True: 31.9k, False: 1.80k]
  ------------------
  111|       |#if SIMDUTF_CPLUSPLUS23
  112|       |    if !consteval
  113|       |#endif
  114|  31.9k|    {
  115|       |      // try to convert the next block of 8 bytes
  116|  31.9k|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (116:11): [True: 25.9k, False: 5.96k]
  ------------------
  117|       |                            // they are ascii
  118|  25.9k|        uint64_t v;
  119|  25.9k|        ::memcpy(&v, data + pos, sizeof(uint64_t));
  120|       |        if constexpr (!match_system(big_endian))
  121|  25.9k|          v = (v >> 8) | (v << (64 - 8));
  122|  25.9k|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (122:13): [True: 1.35k, False: 24.6k]
  ------------------
  123|  1.35k|          size_t final_pos = pos + 4;
  124|  6.75k|          while (pos < final_pos) {
  ------------------
  |  Branch (124:18): [True: 5.40k, False: 1.35k]
  ------------------
  125|  5.40k|            if (check_output && size_t(end - utf8_output) < 1) {
  ------------------
  |  Branch (125:17): [Folded, False: 5.40k]
  |  Branch (125:33): [True: 0, False: 0]
  ------------------
  126|      0|              return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  127|      0|                                 utf8_output - start);
  128|      0|            }
  129|  5.40k|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (129:30): [True: 5.40k, Folded]
  ------------------
  130|  5.40k|                                 ? char(u16_swap_bytes(data[pos]))
  131|  5.40k|                                 : char(data[pos]);
  132|  5.40k|            pos++;
  133|  5.40k|          }
  134|  1.35k|          continue;
  135|  1.35k|        }
  136|  25.9k|      }
  137|  31.9k|    }
  138|       |
  139|  30.5k|    uint16_t word =
  140|  30.5k|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (140:9): [True: 30.5k, Folded]
  ------------------
  141|  30.5k|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (141:9): [True: 3.52k, False: 27.0k]
  ------------------
  142|       |      // will generate one UTF-8 bytes
  143|  3.52k|      if (check_output && size_t(end - utf8_output) < 1) {
  ------------------
  |  Branch (143:11): [Folded, False: 3.52k]
  |  Branch (143:27): [True: 0, False: 0]
  ------------------
  144|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  145|      0|                           utf8_output - start);
  146|      0|      }
  147|  3.52k|      *utf8_output++ = char(word);
  148|  3.52k|      pos++;
  149|  27.0k|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (149:16): [True: 2.30k, False: 24.7k]
  ------------------
  150|       |      // will generate two UTF-8 bytes
  151|       |      // we have 0b110XXXXX 0b10XXXXXX
  152|  2.30k|      if (check_output && size_t(end - utf8_output) < 2) {
  ------------------
  |  Branch (152:11): [Folded, False: 2.30k]
  |  Branch (152:27): [True: 0, False: 0]
  ------------------
  153|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  154|      0|                           utf8_output - start);
  155|      0|      }
  156|  2.30k|      *utf8_output++ = char((word >> 6) | 0b11000000);
  157|  2.30k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  158|  2.30k|      pos++;
  159|       |
  160|  24.7k|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (160:16): [True: 16.9k, False: 7.75k]
  ------------------
  161|       |      // will generate three UTF-8 bytes
  162|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
  163|  16.9k|      if (check_output && size_t(end - utf8_output) < 3) {
  ------------------
  |  Branch (163:11): [Folded, False: 16.9k]
  |  Branch (163:27): [True: 0, False: 0]
  ------------------
  164|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  165|      0|                           utf8_output - start);
  166|      0|      }
  167|  16.9k|      *utf8_output++ = char((word >> 12) | 0b11100000);
  168|  16.9k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  169|  16.9k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  170|  16.9k|      pos++;
  171|  16.9k|    } else {
  172|       |
  173|  7.75k|      if (check_output && size_t(end - utf8_output) < 4) {
  ------------------
  |  Branch (173:11): [Folded, False: 7.75k]
  |  Branch (173:27): [True: 0, False: 0]
  ------------------
  174|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  175|      0|                           utf8_output - start);
  176|      0|      }
  177|       |      // must be a surrogate pair
  178|  7.75k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (178:11): [True: 606, False: 7.15k]
  ------------------
  179|    606|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  180|    606|      }
  181|  7.15k|      uint16_t diff = uint16_t(word - 0xD800);
  182|  7.15k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (182:11): [True: 2.23k, False: 4.91k]
  ------------------
  183|  2.23k|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  184|  2.23k|      }
  185|  4.91k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (185:28): [True: 4.91k, Folded]
  ------------------
  186|  4.91k|                               ? u16_swap_bytes(data[pos + 1])
  187|  4.91k|                               : data[pos + 1];
  188|  4.91k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
  189|  4.91k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (189:11): [True: 4.12k, False: 785]
  ------------------
  190|  4.12k|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  191|  4.12k|      }
  192|    785|      uint32_t value = (diff << 10) + diff2 + 0x10000;
  193|       |      // will generate four UTF-8 bytes
  194|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
  195|    785|      *utf8_output++ = char((value >> 18) | 0b11110000);
  196|    785|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  197|    785|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  198|    785|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
  199|    785|      pos += 2;
  200|    785|    }
  201|  30.5k|  }
  202|  1.80k|  return full_result(error_code::SUCCESS, pos, utf8_output - start);
  203|  8.77k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf824convert_with_replacementILNS_10endiannessE0EEEmPKDsmPc:
  214|  1.27k|                                                    char *utf8_output) {
  215|  1.27k|  size_t pos = 0;
  216|  1.27k|  char *start = utf8_output;
  217|  23.4M|  while (pos < len) {
  ------------------
  |  Branch (217:10): [True: 23.4M, False: 1.27k]
  ------------------
  218|       |#if SIMDUTF_CPLUSPLUS23
  219|       |    if !consteval
  220|       |#endif
  221|  23.4M|    {
  222|       |      // try to convert the next block of 8 bytes
  223|  23.4M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (223:11): [True: 23.4M, False: 3.16k]
  ------------------
  224|       |                            // they are ascii
  225|  23.4M|        uint64_t v;
  226|  23.4M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
  227|       |        if constexpr (!match_system(big_endian)) {
  228|       |          v = (v >> 8) | (v << (64 - 8));
  229|       |        }
  230|  23.4M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (230:13): [True: 3.11M, False: 20.3M]
  ------------------
  231|  3.11M|          size_t final_pos = pos + 4;
  232|  15.5M|          while (pos < final_pos) {
  ------------------
  |  Branch (232:18): [True: 12.4M, False: 3.11M]
  ------------------
  233|  12.4M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (233:30): [Folded, False: 12.4M]
  ------------------
  234|  12.4M|                                 ? char(u16_swap_bytes(data[pos]))
  235|  12.4M|                                 : char(data[pos]);
  236|  12.4M|            pos++;
  237|  12.4M|          }
  238|  3.11M|          continue;
  239|  3.11M|        }
  240|  23.4M|      }
  241|  23.4M|    }
  242|  20.3M|    uint16_t word =
  243|  20.3M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (243:9): [Folded, False: 20.3M]
  ------------------
  244|  20.3M|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (244:9): [True: 264k, False: 20.0M]
  ------------------
  245|       |      // will generate one UTF-8 bytes
  246|   264k|      *utf8_output++ = char(word);
  247|   264k|      pos++;
  248|  20.0M|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (248:16): [True: 661k, False: 19.3M]
  ------------------
  249|       |      // will generate two UTF-8 bytes
  250|       |      // we have 0b110XXXXX 0b10XXXXXX
  251|   661k|      *utf8_output++ = char((word >> 6) | 0b11000000);
  252|   661k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  253|   661k|      pos++;
  254|  19.3M|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (254:16): [True: 16.8M, False: 2.49M]
  ------------------
  255|       |      // will generate three UTF-8 bytes
  256|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
  257|  16.8M|      *utf8_output++ = char((word >> 12) | 0b11100000);
  258|  16.8M|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  259|  16.8M|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  260|  16.8M|      pos++;
  261|  16.8M|    } else {
  262|       |      // surrogate range
  263|  2.49M|      uint16_t diff = uint16_t(word - 0xD800);
  264|  2.49M|      if (diff <= 0x3FF && pos + 1 < len) {
  ------------------
  |  Branch (264:11): [True: 990k, False: 1.50M]
  |  Branch (264:28): [True: 990k, False: 157]
  ------------------
  265|       |        // high surrogate, check for valid pair
  266|   990k|        uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (266:30): [Folded, False: 990k]
  ------------------
  267|   990k|                                 ? u16_swap_bytes(data[pos + 1])
  268|   990k|                                 : data[pos + 1];
  269|   990k|        uint16_t diff2 = uint16_t(next_word - 0xDC00);
  270|   990k|        if (diff2 <= 0x3FF) {
  ------------------
  |  Branch (270:13): [True: 31.5k, False: 958k]
  ------------------
  271|       |          // valid surrogate pair
  272|  31.5k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  273|       |          // will generate four UTF-8 bytes
  274|  31.5k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  275|  31.5k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  276|  31.5k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  277|  31.5k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  278|  31.5k|          pos += 2;
  279|  31.5k|          continue;
  280|  31.5k|        }
  281|   990k|      }
  282|       |      // unpaired surrogate: replace with U+FFFD (0xEF 0xBF 0xBD)
  283|  2.46M|      *utf8_output++ = char(0xef);
  284|  2.46M|      *utf8_output++ = char(0xbf);
  285|  2.46M|      *utf8_output++ = char(0xbd);
  286|  2.46M|      pos++;
  287|  2.46M|    }
  288|  20.3M|  }
  289|  1.27k|  return utf8_output - start;
  290|  1.27k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf824convert_with_replacementILNS_10endiannessE1EEEmPKDsmPc:
  214|  1.20k|                                                    char *utf8_output) {
  215|  1.20k|  size_t pos = 0;
  216|  1.20k|  char *start = utf8_output;
  217|  17.0M|  while (pos < len) {
  ------------------
  |  Branch (217:10): [True: 17.0M, False: 1.20k]
  ------------------
  218|       |#if SIMDUTF_CPLUSPLUS23
  219|       |    if !consteval
  220|       |#endif
  221|  17.0M|    {
  222|       |      // try to convert the next block of 8 bytes
  223|  17.0M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (223:11): [True: 17.0M, False: 2.96k]
  ------------------
  224|       |                            // they are ascii
  225|  17.0M|        uint64_t v;
  226|  17.0M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
  227|  17.0M|        if constexpr (!match_system(big_endian)) {
  228|  17.0M|          v = (v >> 8) | (v << (64 - 8));
  229|  17.0M|        }
  230|  17.0M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (230:13): [True: 3.01M, False: 14.0M]
  ------------------
  231|  3.01M|          size_t final_pos = pos + 4;
  232|  15.0M|          while (pos < final_pos) {
  ------------------
  |  Branch (232:18): [True: 12.0M, False: 3.01M]
  ------------------
  233|  12.0M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (233:30): [True: 12.0M, Folded]
  ------------------
  234|  12.0M|                                 ? char(u16_swap_bytes(data[pos]))
  235|  12.0M|                                 : char(data[pos]);
  236|  12.0M|            pos++;
  237|  12.0M|          }
  238|  3.01M|          continue;
  239|  3.01M|        }
  240|  17.0M|      }
  241|  17.0M|    }
  242|  14.0M|    uint16_t word =
  243|  14.0M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (243:9): [True: 14.0M, Folded]
  ------------------
  244|  14.0M|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (244:9): [True: 198k, False: 13.8M]
  ------------------
  245|       |      // will generate one UTF-8 bytes
  246|   198k|      *utf8_output++ = char(word);
  247|   198k|      pos++;
  248|  13.8M|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (248:16): [True: 218k, False: 13.6M]
  ------------------
  249|       |      // will generate two UTF-8 bytes
  250|       |      // we have 0b110XXXXX 0b10XXXXXX
  251|   218k|      *utf8_output++ = char((word >> 6) | 0b11000000);
  252|   218k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  253|   218k|      pos++;
  254|  13.6M|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (254:16): [True: 11.4M, False: 2.18M]
  ------------------
  255|       |      // will generate three UTF-8 bytes
  256|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
  257|  11.4M|      *utf8_output++ = char((word >> 12) | 0b11100000);
  258|  11.4M|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  259|  11.4M|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  260|  11.4M|      pos++;
  261|  11.4M|    } else {
  262|       |      // surrogate range
  263|  2.18M|      uint16_t diff = uint16_t(word - 0xD800);
  264|  2.18M|      if (diff <= 0x3FF && pos + 1 < len) {
  ------------------
  |  Branch (264:11): [True: 1.23M, False: 948k]
  |  Branch (264:28): [True: 1.23M, False: 164]
  ------------------
  265|       |        // high surrogate, check for valid pair
  266|  1.23M|        uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (266:30): [True: 1.23M, Folded]
  ------------------
  267|  1.23M|                                 ? u16_swap_bytes(data[pos + 1])
  268|  1.23M|                                 : data[pos + 1];
  269|  1.23M|        uint16_t diff2 = uint16_t(next_word - 0xDC00);
  270|  1.23M|        if (diff2 <= 0x3FF) {
  ------------------
  |  Branch (270:13): [True: 64.4k, False: 1.17M]
  ------------------
  271|       |          // valid surrogate pair
  272|  64.4k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  273|       |          // will generate four UTF-8 bytes
  274|  64.4k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  275|  64.4k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  276|  64.4k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  277|  64.4k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  278|  64.4k|          pos += 2;
  279|  64.4k|          continue;
  280|  64.4k|        }
  281|  1.23M|      }
  282|       |      // unpaired surrogate: replace with U+FFFD (0xEF 0xBF 0xBD)
  283|  2.11M|      *utf8_output++ = char(0xef);
  284|  2.11M|      *utf8_output++ = char(0xbf);
  285|  2.11M|      *utf8_output++ = char(0xbd);
  286|  2.11M|      pos++;
  287|  2.11M|    }
  288|  14.0M|  }
  289|  1.20k|  return utf8_output - start;
  290|  1.20k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf88validateEPKcm:
  113|  2.48k|                                                        size_t len) noexcept {
  114|  2.48k|  return validate(reinterpret_cast<const uint8_t *>(buf), len);
  115|  2.48k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf88validateIPKhEEbT_m:
   14|  2.48k|                                                      size_t len) noexcept {
   15|  2.48k|  static_assert(
   16|  2.48k|      std::is_same<typename std::decay<decltype(*data)>::type, uint8_t>::value,
   17|  2.48k|      "dereferencing the data pointer must result in a uint8_t");
   18|  2.48k|  uint64_t pos = 0;
   19|  2.48k|  uint32_t code_point = 0;
   20|  35.3M|  while (pos < len) {
  ------------------
  |  Branch (20:10): [True: 35.3M, False: 2.11k]
  ------------------
   21|  35.3M|    uint64_t next_pos;
   22|       |#if SIMDUTF_CPLUSPLUS23
   23|       |    if !consteval
   24|       |#endif
   25|  35.3M|    { // check if the next 16 bytes are ascii.
   26|  35.3M|      next_pos = pos + 16;
   27|  35.3M|      if (next_pos <= len) { // if it is safe to read 16 more bytes, check
  ------------------
  |  Branch (27:11): [True: 35.3M, False: 9.30k]
  ------------------
   28|       |                             // that they are ascii
   29|  35.3M|        uint64_t v1{};
   30|  35.3M|        std::memcpy(&v1, data + pos, sizeof(uint64_t));
   31|  35.3M|        uint64_t v2{};
   32|  35.3M|        std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   33|  35.3M|        uint64_t v{v1 | v2};
   34|  35.3M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (34:13): [True: 1.49M, False: 33.8M]
  ------------------
   35|  1.49M|          pos = next_pos;
   36|  1.49M|          continue;
   37|  1.49M|        }
   38|  35.3M|      }
   39|  35.3M|    }
   40|       |
   41|  33.8M|    unsigned char byte = data[pos];
   42|       |
   43|  34.9M|    while (byte < 0b10000000) {
  ------------------
  |  Branch (43:12): [True: 1.05M, False: 33.8M]
  ------------------
   44|  1.05M|      if (++pos == len) {
  ------------------
  |  Branch (44:11): [True: 364, False: 1.05M]
  ------------------
   45|    364|        return true;
   46|    364|      }
   47|  1.05M|      byte = data[pos];
   48|  1.05M|    }
   49|       |
   50|  33.8M|    if ((byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (50:9): [True: 879k, False: 32.9M]
  ------------------
   51|   879k|      next_pos = pos + 2;
   52|   879k|      if (next_pos > len) {
  ------------------
  |  Branch (52:11): [True: 0, False: 879k]
  ------------------
   53|      0|        return false;
   54|      0|      }
   55|   879k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (55:11): [True: 0, False: 879k]
  ------------------
   56|      0|        return false;
   57|      0|      }
   58|       |      // range check
   59|   879k|      code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
   60|   879k|      if (code_point < 0x80) {
  ------------------
  |  Branch (60:11): [True: 0, False: 879k]
  ------------------
   61|      0|        return false;
   62|      0|      }
   63|  32.9M|    } else if ((byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (63:16): [True: 32.9M, False: 96.0k]
  ------------------
   64|  32.9M|      next_pos = pos + 3;
   65|  32.9M|      if (next_pos > len) {
  ------------------
  |  Branch (65:11): [True: 0, False: 32.9M]
  ------------------
   66|      0|        return false;
   67|      0|      }
   68|  32.9M|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (68:11): [True: 0, False: 32.9M]
  ------------------
   69|      0|        return false;
   70|      0|      }
   71|  32.9M|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (71:11): [True: 0, False: 32.9M]
  ------------------
   72|      0|        return false;
   73|      0|      }
   74|       |      // range check
   75|  32.9M|      code_point = (byte & 0b00001111) << 12 |
   76|  32.9M|                   (data[pos + 1] & 0b00111111) << 6 |
   77|  32.9M|                   (data[pos + 2] & 0b00111111);
   78|  32.9M|      if ((code_point < 0x800) ||
  ------------------
  |  Branch (78:11): [True: 0, False: 32.9M]
  ------------------
   79|  32.9M|          (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (79:12): [True: 5.86M, False: 27.0M]
  |  Branch (79:35): [True: 0, False: 5.86M]
  ------------------
   80|      0|        return false;
   81|      0|      }
   82|  32.9M|    } else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (82:16): [True: 96.0k, False: 0]
  ------------------
   83|  96.0k|      next_pos = pos + 4;
   84|  96.0k|      if (next_pos > len) {
  ------------------
  |  Branch (84:11): [True: 0, False: 96.0k]
  ------------------
   85|      0|        return false;
   86|      0|      }
   87|  96.0k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (87:11): [True: 0, False: 96.0k]
  ------------------
   88|      0|        return false;
   89|      0|      }
   90|  96.0k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (90:11): [True: 0, False: 96.0k]
  ------------------
   91|      0|        return false;
   92|      0|      }
   93|  96.0k|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (93:11): [True: 0, False: 96.0k]
  ------------------
   94|      0|        return false;
   95|      0|      }
   96|       |      // range check
   97|  96.0k|      code_point =
   98|  96.0k|          (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
   99|  96.0k|          (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
  100|  96.0k|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (100:11): [True: 0, False: 96.0k]
  |  Branch (100:35): [True: 0, False: 96.0k]
  ------------------
  101|      0|        return false;
  102|      0|      }
  103|  96.0k|    } else {
  104|       |      // we may have a continuation
  105|      0|      return false;
  106|      0|    }
  107|  33.8M|    pos = next_pos;
  108|  33.8M|  }
  109|  2.11k|  return true;
  110|  2.48k|}

_ZNK7simdutf8fallback14implementation13validate_utf8EPKcm:
   37|  2.48k|implementation::validate_utf8(const char *buf, size_t len) const noexcept {
   38|  2.48k|  return scalar::utf8::validate(buf, len);
   39|  2.48k|}
_ZNK7simdutf8fallback14implementation24utf8_length_from_utf16leEPKDsm:
  457|    452|    const char16_t *input, size_t length) const noexcept {
  458|    452|  return scalar::utf16::utf8_length_from_utf16<endianness::LITTLE>(input,
  459|    452|                                                                   length);
  460|    452|}
_ZNK7simdutf8fallback14implementation24utf8_length_from_utf16beEPKDsm:
  463|    402|    const char16_t *input, size_t length) const noexcept {
  464|    402|  return scalar::utf16::utf8_length_from_utf16<endianness::BIG>(input, length);
  465|    402|}
_ZNK7simdutf8fallback14implementation41utf8_length_from_utf16le_with_replacementEPKDsm:
  488|  1.27k|    const char16_t *input, size_t length) const noexcept {
  489|  1.27k|  return scalar::utf16::utf8_length_from_utf16_with_replacement<
  490|  1.27k|      endianness::LITTLE>(input, length);
  491|  1.27k|}
_ZNK7simdutf8fallback14implementation41utf8_length_from_utf16be_with_replacementEPKDsm:
  495|  1.20k|    const char16_t *input, size_t length) const noexcept {
  496|  1.20k|  return scalar::utf16::utf8_length_from_utf16_with_replacement<
  497|  1.20k|      endianness::BIG>(input, length);
  498|  1.20k|}
_ZNK7simdutf8fallback14implementation40convert_utf16le_to_utf8_with_replacementEPKDsmPc:
  502|  1.27k|    const char16_t *input, size_t length, char *utf8_buffer) const noexcept {
  503|  1.27k|  return scalar::utf16_to_utf8::convert_with_replacement<endianness::LITTLE>(
  504|  1.27k|      input, length, utf8_buffer);
  505|  1.27k|}
_ZNK7simdutf8fallback14implementation40convert_utf16be_to_utf8_with_replacementEPKDsmPc:
  509|  1.20k|    const char16_t *input, size_t length, char *utf8_buffer) const noexcept {
  510|  1.20k|  return scalar::utf16_to_utf8::convert_with_replacement<endianness::BIG>(
  511|  1.20k|      input, length, utf8_buffer);
  512|  1.20k|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_116buf_block_readerILm64EEC2EPKhm:
   81|  2.48k|    : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE},
  ------------------
  |  Branch (81:42): [True: 1.33k, False: 1.15k]
  ------------------
   82|  2.48k|      idx{0} {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_116buf_block_readerILm64EE14has_full_blockEv:
   90|  1.96M|simdutf_really_inline bool buf_block_reader<STEP_SIZE>::has_full_block() const {
   91|  1.96M|  return idx < lenminusstep;
   92|  1.96M|}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_116buf_block_readerILm64EE10full_blockEv:
   96|  1.96M|buf_block_reader<STEP_SIZE>::full_block() const {
   97|  1.96M|  return &buf[idx];
   98|  1.96M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_116buf_block_readerILm64EE7advanceEv:
  114|  1.96M|simdutf_really_inline void buf_block_reader<STEP_SIZE>::advance() {
  115|  1.96M|  idx += STEP_SIZE;
  116|  1.96M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_116buf_block_readerILm64EE11block_indexEv:
   85|  2.48k|simdutf_really_inline size_t buf_block_reader<STEP_SIZE>::block_index() {
   86|  2.48k|  return idx;
   87|  2.48k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_116buf_block_readerILm64EEC2EPKhm:
   81|  2.48k|    : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE},
  ------------------
  |  Branch (81:42): [True: 1.33k, False: 1.15k]
  ------------------
   82|  2.48k|      idx{0} {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_116buf_block_readerILm64EE14has_full_blockEv:
   90|  1.96M|simdutf_really_inline bool buf_block_reader<STEP_SIZE>::has_full_block() const {
   91|  1.96M|  return idx < lenminusstep;
   92|  1.96M|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_116buf_block_readerILm64EE10full_blockEv:
   96|  1.96M|buf_block_reader<STEP_SIZE>::full_block() const {
   97|  1.96M|  return &buf[idx];
   98|  1.96M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_116buf_block_readerILm64EE7advanceEv:
  114|  1.96M|simdutf_really_inline void buf_block_reader<STEP_SIZE>::advance() {
  115|  1.96M|  idx += STEP_SIZE;
  116|  1.96M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_116buf_block_readerILm64EE11block_indexEv:
   85|  2.48k|simdutf_really_inline size_t buf_block_reader<STEP_SIZE>::block_index() {
   86|  2.48k|  return idx;
   87|  2.48k|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1631utf8_length_from_utf16_bytemaskILNS_10endiannessE0EEEmPKDsm:
   10|    452|                                                             size_t size) {
   11|    452|  size_t pos = 0;
   12|       |
   13|    452|  using vector_u16 = simd16<uint16_t>;
   14|    452|  constexpr size_t N = vector_u16::ELEMENTS;
   15|       |
   16|    452|  const auto one = vector_u16::splat(1);
   17|       |
   18|    452|  auto v_count = vector_u16::zero();
   19|       |
   20|       |  // each char16 yields at least one byte
   21|    452|  size_t count = size / N * N;
   22|       |
   23|       |  // in a single iteration the increment is 0, 1 or 2, despite we have
   24|       |  // three additions
   25|    452|  constexpr size_t max_iterations = 65535 / 2;
   26|    452|  size_t iteration = max_iterations;
   27|       |
   28|  1.01M|  for (; pos < size / N * N; pos += N) {
  ------------------
  |  Branch (28:10): [True: 1.01M, False: 452]
  ------------------
   29|  1.01M|    auto input = vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos));
   30|       |    if constexpr (!match_system(big_endian)) {
   31|       |      input = input.swap_bytes();
   32|       |    }
   33|       |    // 0xd800 .. 0xdbff - low surrogate
   34|       |    // 0xdc00 .. 0xdfff - high surrogate
   35|  1.01M|    const auto is_surrogate = ((input & uint16_t(0xf800)) == uint16_t(0xd800));
   36|       |
   37|       |    // c0 - chars that yield 2- or 3-byte UTF-8 codes
   38|  1.01M|    const auto c0 = min(input & uint16_t(0xff80), one);
   39|       |
   40|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
   41|  1.01M|    const auto c1 = min(input & uint16_t(0xf800), one);
   42|       |
   43|       |    /*
   44|       |        Explanation how the counting works.
   45|       |
   46|       |        In the case of a non-surrogate character we count:
   47|       |        * always 1 -- see how `count` is initialized above;
   48|       |        * c0 = 1 if the current char yields 2 or 3 bytes;
   49|       |        * c1 = 1 if the current char yields 3 bytes.
   50|       |
   51|       |        Thus, we always have correct count for the current char:
   52|       |        from 1, 2 or 3 bytes.
   53|       |
   54|       |        A trickier part is how we count surrogate pairs. Whether
   55|       |        we encounter a surrogate (low or high), we count it as
   56|       |        3 chars and then minus 1 (`is_surrogate` is -1 or 0).
   57|       |        Each surrogate char yields 2. A surrogate pair, that
   58|       |        is a low surrogate followed by a high one, yields
   59|       |        the expected 4 bytes.
   60|       |
   61|       |        It also correctly handles cases when low surrogate is
   62|       |        processed by the this loop, but high surrogate is counted
   63|       |        by the scalar procedure. The scalar procedure uses exactly
   64|       |        the described approach, thanks to that for valid UTF-16
   65|       |        strings it always count correctly.
   66|       |    */
   67|  1.01M|    v_count += c0;
   68|  1.01M|    v_count += c1;
   69|  1.01M|    v_count += vector_u16(is_surrogate);
   70|       |
   71|  1.01M|    iteration -= 1;
   72|  1.01M|    if (iteration == 0) {
  ------------------
  |  Branch (72:9): [True: 22, False: 1.01M]
  ------------------
   73|     22|      count += v_count.sum();
   74|     22|      v_count = vector_u16::zero();
   75|     22|      iteration = max_iterations;
   76|     22|    }
   77|  1.01M|  }
   78|       |
   79|    452|  if (iteration > 0) {
  ------------------
  |  Branch (79:7): [True: 452, False: 0]
  ------------------
   80|    452|    count += v_count.sum();
   81|    452|  }
   82|       |
   83|    452|  return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos,
   84|    452|                                                                   size - pos);
   85|    452|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1631utf8_length_from_utf16_bytemaskILNS_10endiannessE1EEEmPKDsm:
   10|    402|                                                             size_t size) {
   11|    402|  size_t pos = 0;
   12|       |
   13|    402|  using vector_u16 = simd16<uint16_t>;
   14|    402|  constexpr size_t N = vector_u16::ELEMENTS;
   15|       |
   16|    402|  const auto one = vector_u16::splat(1);
   17|       |
   18|    402|  auto v_count = vector_u16::zero();
   19|       |
   20|       |  // each char16 yields at least one byte
   21|    402|  size_t count = size / N * N;
   22|       |
   23|       |  // in a single iteration the increment is 0, 1 or 2, despite we have
   24|       |  // three additions
   25|    402|  constexpr size_t max_iterations = 65535 / 2;
   26|    402|  size_t iteration = max_iterations;
   27|       |
   28|   521k|  for (; pos < size / N * N; pos += N) {
  ------------------
  |  Branch (28:10): [True: 521k, False: 402]
  ------------------
   29|   521k|    auto input = vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos));
   30|   521k|    if constexpr (!match_system(big_endian)) {
   31|   521k|      input = input.swap_bytes();
   32|   521k|    }
   33|       |    // 0xd800 .. 0xdbff - low surrogate
   34|       |    // 0xdc00 .. 0xdfff - high surrogate
   35|   521k|    const auto is_surrogate = ((input & uint16_t(0xf800)) == uint16_t(0xd800));
   36|       |
   37|       |    // c0 - chars that yield 2- or 3-byte UTF-8 codes
   38|   521k|    const auto c0 = min(input & uint16_t(0xff80), one);
   39|       |
   40|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
   41|   521k|    const auto c1 = min(input & uint16_t(0xf800), one);
   42|       |
   43|       |    /*
   44|       |        Explanation how the counting works.
   45|       |
   46|       |        In the case of a non-surrogate character we count:
   47|       |        * always 1 -- see how `count` is initialized above;
   48|       |        * c0 = 1 if the current char yields 2 or 3 bytes;
   49|       |        * c1 = 1 if the current char yields 3 bytes.
   50|       |
   51|       |        Thus, we always have correct count for the current char:
   52|       |        from 1, 2 or 3 bytes.
   53|       |
   54|       |        A trickier part is how we count surrogate pairs. Whether
   55|       |        we encounter a surrogate (low or high), we count it as
   56|       |        3 chars and then minus 1 (`is_surrogate` is -1 or 0).
   57|       |        Each surrogate char yields 2. A surrogate pair, that
   58|       |        is a low surrogate followed by a high one, yields
   59|       |        the expected 4 bytes.
   60|       |
   61|       |        It also correctly handles cases when low surrogate is
   62|       |        processed by the this loop, but high surrogate is counted
   63|       |        by the scalar procedure. The scalar procedure uses exactly
   64|       |        the described approach, thanks to that for valid UTF-16
   65|       |        strings it always count correctly.
   66|       |    */
   67|   521k|    v_count += c0;
   68|   521k|    v_count += c1;
   69|   521k|    v_count += vector_u16(is_surrogate);
   70|       |
   71|   521k|    iteration -= 1;
   72|   521k|    if (iteration == 0) {
  ------------------
  |  Branch (72:9): [True: 11, False: 521k]
  ------------------
   73|     11|      count += v_count.sum();
   74|     11|      v_count = vector_u16::zero();
   75|     11|      iteration = max_iterations;
   76|     11|    }
   77|   521k|  }
   78|       |
   79|    402|  if (iteration > 0) {
  ------------------
  |  Branch (79:7): [True: 402, False: 0]
  ------------------
   80|    402|    count += v_count.sum();
   81|    402|  }
   82|       |
   83|    402|  return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos,
   84|    402|                                                                   size - pos);
   85|    402|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1639utf8_length_from_utf16_with_replacementILNS_10endiannessE0EEENS_6resultEPKDsm:
   89|  1.27k|utf8_length_from_utf16_with_replacement(const char16_t *in, size_t size) {
   90|  1.27k|  using vector_u16 = simd16<uint16_t>;
   91|  1.27k|  constexpr size_t N = vector_u16::ELEMENTS;
   92|  1.27k|  if (N + 1 > size) {
  ------------------
  |  Branch (92:7): [True: 442, False: 835]
  ------------------
   93|    442|    return scalar::utf16::utf8_length_from_utf16_with_replacement<big_endian>(
   94|    442|        in, size);
   95|    442|  } // special case for short inputs
   96|    835|  size_t pos = 0;
   97|    835|  bool any_surrogates = false;
   98|       |
   99|    835|  const auto one = vector_u16::splat(1);
  100|       |
  101|    835|  auto v_count = vector_u16::zero();
  102|    835|  auto v_mismatched_count = vector_u16::zero();
  103|       |
  104|    835|  size_t count = 0;
  105|    835|  size_t mismatched_count = 0;
  106|       |
  107|       |  // in a single iteration the increment is 0, 1 or 2, despite we have
  108|       |  // three additions
  109|    835|  constexpr size_t max_iterations = 65535 / 2;
  110|    835|  size_t iteration = max_iterations;
  111|       |
  112|    835|  if (scalar::utf16::is_low_surrogate<big_endian>(in[0])) {
  ------------------
  |  Branch (112:7): [True: 50, False: 785]
  ------------------
  113|     50|    any_surrogates = true;
  114|     50|    mismatched_count += 1;
  115|     50|  }
  116|       |
  117|  2.05M|  for (; pos < (size - 1) / N * N; pos += N) {
  ------------------
  |  Branch (117:10): [True: 2.04M, False: 835]
  ------------------
  118|  2.04M|    auto input = vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos));
  119|       |    if constexpr (!match_system(big_endian)) {
  120|       |      input = input.swap_bytes();
  121|       |    }
  122|       |    // 0xd800 .. 0xdbff - low surrogate
  123|       |    // 0xdc00 .. 0xdfff - high surrogate
  124|  2.04M|    const auto is_surrogate = ((input & uint16_t(0xf800)) == uint16_t(0xd800));
  125|       |
  126|       |    // c0 - chars that yield 2- or 3-byte UTF-8 codes
  127|  2.04M|    const auto c0 = min(input & uint16_t(0xff80), one);
  128|       |
  129|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
  130|  2.04M|    const auto c1 = min(input & uint16_t(0xf800), one);
  131|       |
  132|  2.04M|    v_count += c0;
  133|  2.04M|    v_count += c1;
  134|  2.04M|    v_count += vector_u16(is_surrogate);
  135|  2.04M|    if (is_surrogate.to_bitmask() != 0 ||
  ------------------
  |  Branch (135:9): [True: 230k, False: 1.81M]
  ------------------
  136|  1.81M|        scalar::utf16::is_low_surrogate<big_endian>(in[pos + N])) {
  ------------------
  |  Branch (136:9): [True: 1.66k, False: 1.81M]
  ------------------
  137|   231k|      any_surrogates = true;
  138|   231k|      auto input_next =
  139|   231k|          vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos + 1));
  140|       |      if constexpr (!match_system(big_endian)) {
  141|       |        input_next = input_next.swap_bytes();
  142|       |      }
  143|       |
  144|   231k|      const auto lb_masked = input & (0xfc00);
  145|   231k|      const auto block_masked = input_next & (0xfc00);
  146|       |
  147|   231k|      const auto lb_is_high = lb_masked == (0xd800);
  148|   231k|      const auto block_is_low = block_masked == (0xdc00);
  149|       |
  150|   231k|      const auto illseq = min(vector_u16(lb_is_high ^ block_is_low), one);
  151|       |
  152|   231k|      v_mismatched_count += illseq;
  153|   231k|    }
  154|       |
  155|  2.04M|    iteration -= 1;
  156|  2.04M|    if (iteration == 0) {
  ------------------
  |  Branch (156:9): [True: 36, False: 2.04M]
  ------------------
  157|     36|      count += v_count.sum();
  158|     36|      v_count = vector_u16::zero();
  159|     36|      mismatched_count += v_mismatched_count.sum();
  160|     36|      v_mismatched_count = vector_u16::zero();
  161|     36|      iteration = max_iterations;
  162|     36|    }
  163|  2.04M|  }
  164|       |
  165|    835|  if (iteration > 0) {
  ------------------
  |  Branch (165:7): [True: 835, False: 0]
  ------------------
  166|    835|    count += v_count.sum();
  167|    835|    mismatched_count += v_mismatched_count.sum();
  168|    835|  }
  169|       |
  170|    835|  if (scalar::utf16::is_low_surrogate<big_endian>(in[pos])) {
  ------------------
  |  Branch (170:7): [True: 103, False: 732]
  ------------------
  171|    103|    any_surrogates = true;
  172|    103|    if (!scalar::utf16::is_high_surrogate<big_endian>(in[pos - 1])) {
  ------------------
  |  Branch (172:9): [True: 78, False: 25]
  ------------------
  173|     78|      mismatched_count -= 1;
  174|     78|      count += 2;
  175|     78|      pos += 1;
  176|     78|    }
  177|    103|  }
  178|    835|  count += pos;
  179|    835|  count += mismatched_count;
  180|    835|  if (scalar::utf16::is_high_surrogate<big_endian>(in[pos - 1])) {
  ------------------
  |  Branch (180:7): [True: 124, False: 711]
  ------------------
  181|    124|    any_surrogates = true;
  182|    124|    if (pos == size) {
  ------------------
  |  Branch (182:9): [True: 0, False: 124]
  ------------------
  183|      0|      count += 2;
  184|    124|    } else if (scalar::utf16::is_low_surrogate<big_endian>(in[pos])) {
  ------------------
  |  Branch (184:16): [True: 25, False: 99]
  ------------------
  185|     25|      pos += 1;
  186|     25|      count += 2;
  187|     25|    }
  188|    124|  }
  189|    835|  result scalar_result =
  190|    835|      scalar::utf16::utf8_length_from_utf16_with_replacement<big_endian>(
  191|    835|          in + pos, size - pos);
  192|    835|  return {any_surrogates ? SURROGATE : scalar_result.error,
  ------------------
  |  Branch (192:11): [True: 513, False: 322]
  ------------------
  193|    835|          count + scalar_result.count};
  194|  1.27k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1639utf8_length_from_utf16_with_replacementILNS_10endiannessE1EEENS_6resultEPKDsm:
   89|  1.20k|utf8_length_from_utf16_with_replacement(const char16_t *in, size_t size) {
   90|  1.20k|  using vector_u16 = simd16<uint16_t>;
   91|  1.20k|  constexpr size_t N = vector_u16::ELEMENTS;
   92|  1.20k|  if (N + 1 > size) {
  ------------------
  |  Branch (92:7): [True: 388, False: 817]
  ------------------
   93|    388|    return scalar::utf16::utf8_length_from_utf16_with_replacement<big_endian>(
   94|    388|        in, size);
   95|    388|  } // special case for short inputs
   96|    817|  size_t pos = 0;
   97|    817|  bool any_surrogates = false;
   98|       |
   99|    817|  const auto one = vector_u16::splat(1);
  100|       |
  101|    817|  auto v_count = vector_u16::zero();
  102|    817|  auto v_mismatched_count = vector_u16::zero();
  103|       |
  104|    817|  size_t count = 0;
  105|    817|  size_t mismatched_count = 0;
  106|       |
  107|       |  // in a single iteration the increment is 0, 1 or 2, despite we have
  108|       |  // three additions
  109|    817|  constexpr size_t max_iterations = 65535 / 2;
  110|    817|  size_t iteration = max_iterations;
  111|       |
  112|    817|  if (scalar::utf16::is_low_surrogate<big_endian>(in[0])) {
  ------------------
  |  Branch (112:7): [True: 48, False: 769]
  ------------------
  113|     48|    any_surrogates = true;
  114|     48|    mismatched_count += 1;
  115|     48|  }
  116|       |
  117|  1.63M|  for (; pos < (size - 1) / N * N; pos += N) {
  ------------------
  |  Branch (117:10): [True: 1.63M, False: 817]
  ------------------
  118|  1.63M|    auto input = vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos));
  119|  1.63M|    if constexpr (!match_system(big_endian)) {
  120|  1.63M|      input = input.swap_bytes();
  121|  1.63M|    }
  122|       |    // 0xd800 .. 0xdbff - low surrogate
  123|       |    // 0xdc00 .. 0xdfff - high surrogate
  124|  1.63M|    const auto is_surrogate = ((input & uint16_t(0xf800)) == uint16_t(0xd800));
  125|       |
  126|       |    // c0 - chars that yield 2- or 3-byte UTF-8 codes
  127|  1.63M|    const auto c0 = min(input & uint16_t(0xff80), one);
  128|       |
  129|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
  130|  1.63M|    const auto c1 = min(input & uint16_t(0xf800), one);
  131|       |
  132|  1.63M|    v_count += c0;
  133|  1.63M|    v_count += c1;
  134|  1.63M|    v_count += vector_u16(is_surrogate);
  135|  1.63M|    if (is_surrogate.to_bitmask() != 0 ||
  ------------------
  |  Branch (135:9): [True: 249k, False: 1.38M]
  ------------------
  136|  1.38M|        scalar::utf16::is_low_surrogate<big_endian>(in[pos + N])) {
  ------------------
  |  Branch (136:9): [True: 2.70k, False: 1.38M]
  ------------------
  137|   252k|      any_surrogates = true;
  138|   252k|      auto input_next =
  139|   252k|          vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos + 1));
  140|   252k|      if constexpr (!match_system(big_endian)) {
  141|   252k|        input_next = input_next.swap_bytes();
  142|   252k|      }
  143|       |
  144|   252k|      const auto lb_masked = input & (0xfc00);
  145|   252k|      const auto block_masked = input_next & (0xfc00);
  146|       |
  147|   252k|      const auto lb_is_high = lb_masked == (0xd800);
  148|   252k|      const auto block_is_low = block_masked == (0xdc00);
  149|       |
  150|   252k|      const auto illseq = min(vector_u16(lb_is_high ^ block_is_low), one);
  151|       |
  152|   252k|      v_mismatched_count += illseq;
  153|   252k|    }
  154|       |
  155|  1.63M|    iteration -= 1;
  156|  1.63M|    if (iteration == 0) {
  ------------------
  |  Branch (156:9): [True: 30, False: 1.63M]
  ------------------
  157|     30|      count += v_count.sum();
  158|     30|      v_count = vector_u16::zero();
  159|     30|      mismatched_count += v_mismatched_count.sum();
  160|     30|      v_mismatched_count = vector_u16::zero();
  161|     30|      iteration = max_iterations;
  162|     30|    }
  163|  1.63M|  }
  164|       |
  165|    817|  if (iteration > 0) {
  ------------------
  |  Branch (165:7): [True: 817, False: 0]
  ------------------
  166|    817|    count += v_count.sum();
  167|    817|    mismatched_count += v_mismatched_count.sum();
  168|    817|  }
  169|       |
  170|    817|  if (scalar::utf16::is_low_surrogate<big_endian>(in[pos])) {
  ------------------
  |  Branch (170:7): [True: 106, False: 711]
  ------------------
  171|    106|    any_surrogates = true;
  172|    106|    if (!scalar::utf16::is_high_surrogate<big_endian>(in[pos - 1])) {
  ------------------
  |  Branch (172:9): [True: 76, False: 30]
  ------------------
  173|     76|      mismatched_count -= 1;
  174|     76|      count += 2;
  175|     76|      pos += 1;
  176|     76|    }
  177|    106|  }
  178|    817|  count += pos;
  179|    817|  count += mismatched_count;
  180|    817|  if (scalar::utf16::is_high_surrogate<big_endian>(in[pos - 1])) {
  ------------------
  |  Branch (180:7): [True: 138, False: 679]
  ------------------
  181|    138|    any_surrogates = true;
  182|    138|    if (pos == size) {
  ------------------
  |  Branch (182:9): [True: 0, False: 138]
  ------------------
  183|      0|      count += 2;
  184|    138|    } else if (scalar::utf16::is_low_surrogate<big_endian>(in[pos])) {
  ------------------
  |  Branch (184:16): [True: 30, False: 108]
  ------------------
  185|     30|      pos += 1;
  186|     30|      count += 2;
  187|     30|    }
  188|    138|  }
  189|    817|  result scalar_result =
  190|    817|      scalar::utf16::utf8_length_from_utf16_with_replacement<big_endian>(
  191|    817|          in + pos, size - pos);
  192|    817|  return {any_surrogates ? SURROGATE : scalar_result.error,
  ------------------
  |  Branch (192:11): [True: 527, False: 290]
  ------------------
  193|    817|          count + scalar_result.count};
  194|  1.20k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1631utf8_length_from_utf16_bytemaskILNS_10endiannessE0EEEmPKDsm:
   10|    452|                                                             size_t size) {
   11|    452|  size_t pos = 0;
   12|       |
   13|    452|  using vector_u16 = simd16<uint16_t>;
   14|    452|  constexpr size_t N = vector_u16::ELEMENTS;
   15|       |
   16|    452|  const auto one = vector_u16::splat(1);
   17|       |
   18|    452|  auto v_count = vector_u16::zero();
   19|       |
   20|       |  // each char16 yields at least one byte
   21|    452|  size_t count = size / N * N;
   22|       |
   23|       |  // in a single iteration the increment is 0, 1 or 2, despite we have
   24|       |  // three additions
   25|    452|  constexpr size_t max_iterations = 65535 / 2;
   26|    452|  size_t iteration = max_iterations;
   27|       |
   28|  2.02M|  for (; pos < size / N * N; pos += N) {
  ------------------
  |  Branch (28:10): [True: 2.02M, False: 452]
  ------------------
   29|  2.02M|    auto input = vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos));
   30|       |    if constexpr (!match_system(big_endian)) {
   31|       |      input = input.swap_bytes();
   32|       |    }
   33|       |    // 0xd800 .. 0xdbff - low surrogate
   34|       |    // 0xdc00 .. 0xdfff - high surrogate
   35|  2.02M|    const auto is_surrogate = ((input & uint16_t(0xf800)) == uint16_t(0xd800));
   36|       |
   37|       |    // c0 - chars that yield 2- or 3-byte UTF-8 codes
   38|  2.02M|    const auto c0 = min(input & uint16_t(0xff80), one);
   39|       |
   40|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
   41|  2.02M|    const auto c1 = min(input & uint16_t(0xf800), one);
   42|       |
   43|       |    /*
   44|       |        Explanation how the counting works.
   45|       |
   46|       |        In the case of a non-surrogate character we count:
   47|       |        * always 1 -- see how `count` is initialized above;
   48|       |        * c0 = 1 if the current char yields 2 or 3 bytes;
   49|       |        * c1 = 1 if the current char yields 3 bytes.
   50|       |
   51|       |        Thus, we always have correct count for the current char:
   52|       |        from 1, 2 or 3 bytes.
   53|       |
   54|       |        A trickier part is how we count surrogate pairs. Whether
   55|       |        we encounter a surrogate (low or high), we count it as
   56|       |        3 chars and then minus 1 (`is_surrogate` is -1 or 0).
   57|       |        Each surrogate char yields 2. A surrogate pair, that
   58|       |        is a low surrogate followed by a high one, yields
   59|       |        the expected 4 bytes.
   60|       |
   61|       |        It also correctly handles cases when low surrogate is
   62|       |        processed by the this loop, but high surrogate is counted
   63|       |        by the scalar procedure. The scalar procedure uses exactly
   64|       |        the described approach, thanks to that for valid UTF-16
   65|       |        strings it always count correctly.
   66|       |    */
   67|  2.02M|    v_count += c0;
   68|  2.02M|    v_count += c1;
   69|  2.02M|    v_count += vector_u16(is_surrogate);
   70|       |
   71|  2.02M|    iteration -= 1;
   72|  2.02M|    if (iteration == 0) {
  ------------------
  |  Branch (72:9): [True: 48, False: 2.02M]
  ------------------
   73|     48|      count += v_count.sum();
   74|     48|      v_count = vector_u16::zero();
   75|     48|      iteration = max_iterations;
   76|     48|    }
   77|  2.02M|  }
   78|       |
   79|    452|  if (iteration > 0) {
  ------------------
  |  Branch (79:7): [True: 452, False: 0]
  ------------------
   80|    452|    count += v_count.sum();
   81|    452|  }
   82|       |
   83|    452|  return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos,
   84|    452|                                                                   size - pos);
   85|    452|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1631utf8_length_from_utf16_bytemaskILNS_10endiannessE1EEEmPKDsm:
   10|    402|                                                             size_t size) {
   11|    402|  size_t pos = 0;
   12|       |
   13|    402|  using vector_u16 = simd16<uint16_t>;
   14|    402|  constexpr size_t N = vector_u16::ELEMENTS;
   15|       |
   16|    402|  const auto one = vector_u16::splat(1);
   17|       |
   18|    402|  auto v_count = vector_u16::zero();
   19|       |
   20|       |  // each char16 yields at least one byte
   21|    402|  size_t count = size / N * N;
   22|       |
   23|       |  // in a single iteration the increment is 0, 1 or 2, despite we have
   24|       |  // three additions
   25|    402|  constexpr size_t max_iterations = 65535 / 2;
   26|    402|  size_t iteration = max_iterations;
   27|       |
   28|  1.04M|  for (; pos < size / N * N; pos += N) {
  ------------------
  |  Branch (28:10): [True: 1.04M, False: 402]
  ------------------
   29|  1.04M|    auto input = vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos));
   30|  1.04M|    if constexpr (!match_system(big_endian)) {
   31|  1.04M|      input = input.swap_bytes();
   32|  1.04M|    }
   33|       |    // 0xd800 .. 0xdbff - low surrogate
   34|       |    // 0xdc00 .. 0xdfff - high surrogate
   35|  1.04M|    const auto is_surrogate = ((input & uint16_t(0xf800)) == uint16_t(0xd800));
   36|       |
   37|       |    // c0 - chars that yield 2- or 3-byte UTF-8 codes
   38|  1.04M|    const auto c0 = min(input & uint16_t(0xff80), one);
   39|       |
   40|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
   41|  1.04M|    const auto c1 = min(input & uint16_t(0xf800), one);
   42|       |
   43|       |    /*
   44|       |        Explanation how the counting works.
   45|       |
   46|       |        In the case of a non-surrogate character we count:
   47|       |        * always 1 -- see how `count` is initialized above;
   48|       |        * c0 = 1 if the current char yields 2 or 3 bytes;
   49|       |        * c1 = 1 if the current char yields 3 bytes.
   50|       |
   51|       |        Thus, we always have correct count for the current char:
   52|       |        from 1, 2 or 3 bytes.
   53|       |
   54|       |        A trickier part is how we count surrogate pairs. Whether
   55|       |        we encounter a surrogate (low or high), we count it as
   56|       |        3 chars and then minus 1 (`is_surrogate` is -1 or 0).
   57|       |        Each surrogate char yields 2. A surrogate pair, that
   58|       |        is a low surrogate followed by a high one, yields
   59|       |        the expected 4 bytes.
   60|       |
   61|       |        It also correctly handles cases when low surrogate is
   62|       |        processed by the this loop, but high surrogate is counted
   63|       |        by the scalar procedure. The scalar procedure uses exactly
   64|       |        the described approach, thanks to that for valid UTF-16
   65|       |        strings it always count correctly.
   66|       |    */
   67|  1.04M|    v_count += c0;
   68|  1.04M|    v_count += c1;
   69|  1.04M|    v_count += vector_u16(is_surrogate);
   70|       |
   71|  1.04M|    iteration -= 1;
   72|  1.04M|    if (iteration == 0) {
  ------------------
  |  Branch (72:9): [True: 25, False: 1.04M]
  ------------------
   73|     25|      count += v_count.sum();
   74|     25|      v_count = vector_u16::zero();
   75|     25|      iteration = max_iterations;
   76|     25|    }
   77|  1.04M|  }
   78|       |
   79|    402|  if (iteration > 0) {
  ------------------
  |  Branch (79:7): [True: 402, False: 0]
  ------------------
   80|    402|    count += v_count.sum();
   81|    402|  }
   82|       |
   83|    402|  return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos,
   84|    402|                                                                   size - pos);
   85|    402|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1639utf8_length_from_utf16_with_replacementILNS_10endiannessE0EEENS_6resultEPKDsm:
   89|  1.27k|utf8_length_from_utf16_with_replacement(const char16_t *in, size_t size) {
   90|  1.27k|  using vector_u16 = simd16<uint16_t>;
   91|  1.27k|  constexpr size_t N = vector_u16::ELEMENTS;
   92|  1.27k|  if (N + 1 > size) {
  ------------------
  |  Branch (92:7): [True: 212, False: 1.06k]
  ------------------
   93|    212|    return scalar::utf16::utf8_length_from_utf16_with_replacement<big_endian>(
   94|    212|        in, size);
   95|    212|  } // special case for short inputs
   96|  1.06k|  size_t pos = 0;
   97|  1.06k|  bool any_surrogates = false;
   98|       |
   99|  1.06k|  const auto one = vector_u16::splat(1);
  100|       |
  101|  1.06k|  auto v_count = vector_u16::zero();
  102|  1.06k|  auto v_mismatched_count = vector_u16::zero();
  103|       |
  104|  1.06k|  size_t count = 0;
  105|  1.06k|  size_t mismatched_count = 0;
  106|       |
  107|       |  // in a single iteration the increment is 0, 1 or 2, despite we have
  108|       |  // three additions
  109|  1.06k|  constexpr size_t max_iterations = 65535 / 2;
  110|  1.06k|  size_t iteration = max_iterations;
  111|       |
  112|  1.06k|  if (scalar::utf16::is_low_surrogate<big_endian>(in[0])) {
  ------------------
  |  Branch (112:7): [True: 65, False: 1.00k]
  ------------------
  113|     65|    any_surrogates = true;
  114|     65|    mismatched_count += 1;
  115|     65|  }
  116|       |
  117|  4.10M|  for (; pos < (size - 1) / N * N; pos += N) {
  ------------------
  |  Branch (117:10): [True: 4.10M, False: 1.06k]
  ------------------
  118|  4.10M|    auto input = vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos));
  119|       |    if constexpr (!match_system(big_endian)) {
  120|       |      input = input.swap_bytes();
  121|       |    }
  122|       |    // 0xd800 .. 0xdbff - low surrogate
  123|       |    // 0xdc00 .. 0xdfff - high surrogate
  124|  4.10M|    const auto is_surrogate = ((input & uint16_t(0xf800)) == uint16_t(0xd800));
  125|       |
  126|       |    // c0 - chars that yield 2- or 3-byte UTF-8 codes
  127|  4.10M|    const auto c0 = min(input & uint16_t(0xff80), one);
  128|       |
  129|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
  130|  4.10M|    const auto c1 = min(input & uint16_t(0xf800), one);
  131|       |
  132|  4.10M|    v_count += c0;
  133|  4.10M|    v_count += c1;
  134|  4.10M|    v_count += vector_u16(is_surrogate);
  135|  4.10M|    if (is_surrogate.to_bitmask() != 0 ||
  ------------------
  |  Branch (135:9): [True: 402k, False: 3.69M]
  ------------------
  136|  3.69M|        scalar::utf16::is_low_surrogate<big_endian>(in[pos + N])) {
  ------------------
  |  Branch (136:9): [True: 4.65k, False: 3.69M]
  ------------------
  137|   406k|      any_surrogates = true;
  138|   406k|      auto input_next =
  139|   406k|          vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos + 1));
  140|       |      if constexpr (!match_system(big_endian)) {
  141|       |        input_next = input_next.swap_bytes();
  142|       |      }
  143|       |
  144|   406k|      const auto lb_masked = input & (0xfc00);
  145|   406k|      const auto block_masked = input_next & (0xfc00);
  146|       |
  147|   406k|      const auto lb_is_high = lb_masked == (0xd800);
  148|   406k|      const auto block_is_low = block_masked == (0xdc00);
  149|       |
  150|   406k|      const auto illseq = min(vector_u16(lb_is_high ^ block_is_low), one);
  151|       |
  152|   406k|      v_mismatched_count += illseq;
  153|   406k|    }
  154|       |
  155|  4.10M|    iteration -= 1;
  156|  4.10M|    if (iteration == 0) {
  ------------------
  |  Branch (156:9): [True: 91, False: 4.10M]
  ------------------
  157|     91|      count += v_count.sum();
  158|     91|      v_count = vector_u16::zero();
  159|     91|      mismatched_count += v_mismatched_count.sum();
  160|     91|      v_mismatched_count = vector_u16::zero();
  161|     91|      iteration = max_iterations;
  162|     91|    }
  163|  4.10M|  }
  164|       |
  165|  1.06k|  if (iteration > 0) {
  ------------------
  |  Branch (165:7): [True: 1.06k, False: 0]
  ------------------
  166|  1.06k|    count += v_count.sum();
  167|  1.06k|    mismatched_count += v_mismatched_count.sum();
  168|  1.06k|  }
  169|       |
  170|  1.06k|  if (scalar::utf16::is_low_surrogate<big_endian>(in[pos])) {
  ------------------
  |  Branch (170:7): [True: 163, False: 902]
  ------------------
  171|    163|    any_surrogates = true;
  172|    163|    if (!scalar::utf16::is_high_surrogate<big_endian>(in[pos - 1])) {
  ------------------
  |  Branch (172:9): [True: 119, False: 44]
  ------------------
  173|    119|      mismatched_count -= 1;
  174|    119|      count += 2;
  175|    119|      pos += 1;
  176|    119|    }
  177|    163|  }
  178|  1.06k|  count += pos;
  179|  1.06k|  count += mismatched_count;
  180|  1.06k|  if (scalar::utf16::is_high_surrogate<big_endian>(in[pos - 1])) {
  ------------------
  |  Branch (180:7): [True: 180, False: 885]
  ------------------
  181|    180|    any_surrogates = true;
  182|    180|    if (pos == size) {
  ------------------
  |  Branch (182:9): [True: 0, False: 180]
  ------------------
  183|      0|      count += 2;
  184|    180|    } else if (scalar::utf16::is_low_surrogate<big_endian>(in[pos])) {
  ------------------
  |  Branch (184:16): [True: 44, False: 136]
  ------------------
  185|     44|      pos += 1;
  186|     44|      count += 2;
  187|     44|    }
  188|    180|  }
  189|  1.06k|  result scalar_result =
  190|  1.06k|      scalar::utf16::utf8_length_from_utf16_with_replacement<big_endian>(
  191|  1.06k|          in + pos, size - pos);
  192|  1.06k|  return {any_surrogates ? SURROGATE : scalar_result.error,
  ------------------
  |  Branch (192:11): [True: 666, False: 399]
  ------------------
  193|  1.06k|          count + scalar_result.count};
  194|  1.27k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1639utf8_length_from_utf16_with_replacementILNS_10endiannessE1EEENS_6resultEPKDsm:
   89|  1.20k|utf8_length_from_utf16_with_replacement(const char16_t *in, size_t size) {
   90|  1.20k|  using vector_u16 = simd16<uint16_t>;
   91|  1.20k|  constexpr size_t N = vector_u16::ELEMENTS;
   92|  1.20k|  if (N + 1 > size) {
  ------------------
  |  Branch (92:7): [True: 215, False: 990]
  ------------------
   93|    215|    return scalar::utf16::utf8_length_from_utf16_with_replacement<big_endian>(
   94|    215|        in, size);
   95|    215|  } // special case for short inputs
   96|    990|  size_t pos = 0;
   97|    990|  bool any_surrogates = false;
   98|       |
   99|    990|  const auto one = vector_u16::splat(1);
  100|       |
  101|    990|  auto v_count = vector_u16::zero();
  102|    990|  auto v_mismatched_count = vector_u16::zero();
  103|       |
  104|    990|  size_t count = 0;
  105|    990|  size_t mismatched_count = 0;
  106|       |
  107|       |  // in a single iteration the increment is 0, 1 or 2, despite we have
  108|       |  // three additions
  109|    990|  constexpr size_t max_iterations = 65535 / 2;
  110|    990|  size_t iteration = max_iterations;
  111|       |
  112|    990|  if (scalar::utf16::is_low_surrogate<big_endian>(in[0])) {
  ------------------
  |  Branch (112:7): [True: 67, False: 923]
  ------------------
  113|     67|    any_surrogates = true;
  114|     67|    mismatched_count += 1;
  115|     67|  }
  116|       |
  117|  3.26M|  for (; pos < (size - 1) / N * N; pos += N) {
  ------------------
  |  Branch (117:10): [True: 3.26M, False: 990]
  ------------------
  118|  3.26M|    auto input = vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos));
  119|  3.26M|    if constexpr (!match_system(big_endian)) {
  120|  3.26M|      input = input.swap_bytes();
  121|  3.26M|    }
  122|       |    // 0xd800 .. 0xdbff - low surrogate
  123|       |    // 0xdc00 .. 0xdfff - high surrogate
  124|  3.26M|    const auto is_surrogate = ((input & uint16_t(0xf800)) == uint16_t(0xd800));
  125|       |
  126|       |    // c0 - chars that yield 2- or 3-byte UTF-8 codes
  127|  3.26M|    const auto c0 = min(input & uint16_t(0xff80), one);
  128|       |
  129|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
  130|  3.26M|    const auto c1 = min(input & uint16_t(0xf800), one);
  131|       |
  132|  3.26M|    v_count += c0;
  133|  3.26M|    v_count += c1;
  134|  3.26M|    v_count += vector_u16(is_surrogate);
  135|  3.26M|    if (is_surrogate.to_bitmask() != 0 ||
  ------------------
  |  Branch (135:9): [True: 411k, False: 2.85M]
  ------------------
  136|  2.85M|        scalar::utf16::is_low_surrogate<big_endian>(in[pos + N])) {
  ------------------
  |  Branch (136:9): [True: 7.01k, False: 2.84M]
  ------------------
  137|   418k|      any_surrogates = true;
  138|   418k|      auto input_next =
  139|   418k|          vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos + 1));
  140|   418k|      if constexpr (!match_system(big_endian)) {
  141|   418k|        input_next = input_next.swap_bytes();
  142|   418k|      }
  143|       |
  144|   418k|      const auto lb_masked = input & (0xfc00);
  145|   418k|      const auto block_masked = input_next & (0xfc00);
  146|       |
  147|   418k|      const auto lb_is_high = lb_masked == (0xd800);
  148|   418k|      const auto block_is_low = block_masked == (0xdc00);
  149|       |
  150|   418k|      const auto illseq = min(vector_u16(lb_is_high ^ block_is_low), one);
  151|       |
  152|   418k|      v_mismatched_count += illseq;
  153|   418k|    }
  154|       |
  155|  3.26M|    iteration -= 1;
  156|  3.26M|    if (iteration == 0) {
  ------------------
  |  Branch (156:9): [True: 71, False: 3.26M]
  ------------------
  157|     71|      count += v_count.sum();
  158|     71|      v_count = vector_u16::zero();
  159|     71|      mismatched_count += v_mismatched_count.sum();
  160|     71|      v_mismatched_count = vector_u16::zero();
  161|     71|      iteration = max_iterations;
  162|     71|    }
  163|  3.26M|  }
  164|       |
  165|    990|  if (iteration > 0) {
  ------------------
  |  Branch (165:7): [True: 990, False: 0]
  ------------------
  166|    990|    count += v_count.sum();
  167|    990|    mismatched_count += v_mismatched_count.sum();
  168|    990|  }
  169|       |
  170|    990|  if (scalar::utf16::is_low_surrogate<big_endian>(in[pos])) {
  ------------------
  |  Branch (170:7): [True: 149, False: 841]
  ------------------
  171|    149|    any_surrogates = true;
  172|    149|    if (!scalar::utf16::is_high_surrogate<big_endian>(in[pos - 1])) {
  ------------------
  |  Branch (172:9): [True: 109, False: 40]
  ------------------
  173|    109|      mismatched_count -= 1;
  174|    109|      count += 2;
  175|    109|      pos += 1;
  176|    109|    }
  177|    149|  }
  178|    990|  count += pos;
  179|    990|  count += mismatched_count;
  180|    990|  if (scalar::utf16::is_high_surrogate<big_endian>(in[pos - 1])) {
  ------------------
  |  Branch (180:7): [True: 184, False: 806]
  ------------------
  181|    184|    any_surrogates = true;
  182|    184|    if (pos == size) {
  ------------------
  |  Branch (182:9): [True: 0, False: 184]
  ------------------
  183|      0|      count += 2;
  184|    184|    } else if (scalar::utf16::is_low_surrogate<big_endian>(in[pos])) {
  ------------------
  |  Branch (184:16): [True: 40, False: 144]
  ------------------
  185|     40|      pos += 1;
  186|     40|      count += 2;
  187|     40|    }
  188|    184|  }
  189|    990|  result scalar_result =
  190|    990|      scalar::utf16::utf8_length_from_utf16_with_replacement<big_endian>(
  191|    990|          in + pos, size - pos);
  192|    990|  return {any_surrogates ? SURROGATE : scalar_result.error,
  ------------------
  |  Branch (192:11): [True: 654, False: 336]
  ------------------
  193|    990|          count + scalar_result.count};
  194|  1.20k|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf16_to_utf828convert_with_replacement_viaIZNKS0_14implementation40convert_utf16le_to_utf8_with_replacementEPKDsmPcE3$_0EEmT_S6_mS7_:
   15|  1.27k|    char *utf8_output) {
   16|  1.27k|  char *const start = utf8_output;
   17|  1.27k|  size_t pos = 0;
   18|  2.46M|  while (pos < len) {
  ------------------
  |  Branch (18:10): [True: 2.46M, False: 338]
  ------------------
   19|  2.46M|    full_result r = convert_with_details(buf + pos, len - pos, utf8_output);
   20|  2.46M|    utf8_output += r.output_count;
   21|  2.46M|    if (r.error != error_code::SURROGATE) {
  ------------------
  |  Branch (21:9): [True: 939, False: 2.46M]
  ------------------
   22|    939|      break;
   23|    939|    }
   24|  2.46M|    pos += r.input_count + 1;
   25|  2.46M|    utf8_output[0] = char(0xef);
   26|  2.46M|    utf8_output[1] = char(0xbf);
   27|  2.46M|    utf8_output[2] = char(0xbd);
   28|  2.46M|    utf8_output += 3;
   29|  2.46M|  }
   30|  1.27k|  return size_t(utf8_output - start);
   31|  1.27k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf16_to_utf828convert_with_replacement_viaIZNKS0_14implementation40convert_utf16be_to_utf8_with_replacementEPKDsmPcE3$_0EEmT_S6_mS7_:
   15|  1.20k|    char *utf8_output) {
   16|  1.20k|  char *const start = utf8_output;
   17|  1.20k|  size_t pos = 0;
   18|  2.11M|  while (pos < len) {
  ------------------
  |  Branch (18:10): [True: 2.11M, False: 305]
  ------------------
   19|  2.11M|    full_result r = convert_with_details(buf + pos, len - pos, utf8_output);
   20|  2.11M|    utf8_output += r.output_count;
   21|  2.11M|    if (r.error != error_code::SURROGATE) {
  ------------------
  |  Branch (21:9): [True: 900, False: 2.11M]
  ------------------
   22|    900|      break;
   23|    900|    }
   24|  2.11M|    pos += r.input_count + 1;
   25|  2.11M|    utf8_output[0] = char(0xef);
   26|  2.11M|    utf8_output[1] = char(0xbf);
   27|  2.11M|    utf8_output[2] = char(0xbd);
   28|  2.11M|    utf8_output += 3;
   29|  2.11M|  }
   30|  1.20k|  return size_t(utf8_output - start);
   31|  1.20k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf16_to_utf828convert_with_replacement_viaIZNKS0_14implementation40convert_utf16le_to_utf8_with_replacementEPKDsmPcE3$_0EEmT_S6_mS7_:
   15|  1.27k|    char *utf8_output) {
   16|  1.27k|  char *const start = utf8_output;
   17|  1.27k|  size_t pos = 0;
   18|  2.46M|  while (pos < len) {
  ------------------
  |  Branch (18:10): [True: 2.46M, False: 338]
  ------------------
   19|  2.46M|    full_result r = convert_with_details(buf + pos, len - pos, utf8_output);
   20|  2.46M|    utf8_output += r.output_count;
   21|  2.46M|    if (r.error != error_code::SURROGATE) {
  ------------------
  |  Branch (21:9): [True: 939, False: 2.46M]
  ------------------
   22|    939|      break;
   23|    939|    }
   24|  2.46M|    pos += r.input_count + 1;
   25|  2.46M|    utf8_output[0] = char(0xef);
   26|  2.46M|    utf8_output[1] = char(0xbf);
   27|  2.46M|    utf8_output[2] = char(0xbd);
   28|  2.46M|    utf8_output += 3;
   29|  2.46M|  }
   30|  1.27k|  return size_t(utf8_output - start);
   31|  1.27k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf16_to_utf828convert_with_replacement_viaIZNKS0_14implementation40convert_utf16be_to_utf8_with_replacementEPKDsmPcE3$_0EEmT_S6_mS7_:
   15|  1.20k|    char *utf8_output) {
   16|  1.20k|  char *const start = utf8_output;
   17|  1.20k|  size_t pos = 0;
   18|  2.11M|  while (pos < len) {
  ------------------
  |  Branch (18:10): [True: 2.11M, False: 305]
  ------------------
   19|  2.11M|    full_result r = convert_with_details(buf + pos, len - pos, utf8_output);
   20|  2.11M|    utf8_output += r.output_count;
   21|  2.11M|    if (r.error != error_code::SURROGATE) {
  ------------------
  |  Branch (21:9): [True: 900, False: 2.11M]
  ------------------
   22|    900|      break;
   23|    900|    }
   24|  2.11M|    pos += r.input_count + 1;
   25|  2.11M|    utf8_output[0] = char(0xef);
   26|  2.11M|    utf8_output[1] = char(0xbf);
   27|  2.11M|    utf8_output[2] = char(0xbd);
   28|  2.11M|    utf8_output += 3;
   29|  2.11M|  }
   30|  1.20k|  return size_t(utf8_output - start);
   31|  1.20k|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation12utf8_checker16check_next_inputERKNS1_4simd8simd8x64IhEE:
  215|  1.96M|  simdutf_really_inline bool check_next_input(const simd8x64<uint8_t> &input) {
  216|  1.96M|    if (simdutf_likely(is_ascii(input))) {
  ------------------
  |  |   90|  1.96M|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (90:31): [True: 358k, False: 1.60M]
  |  |  ------------------
  ------------------
  217|   358k|      this->error |= this->prev_incomplete;
  218|   358k|      return true;
  219|  1.60M|    } else {
  220|       |      // you might think that a for-loop would work, but under Visual Studio, it
  221|       |      // is not good enough.
  222|  1.60M|      static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  223|  1.60M|                        (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  224|  1.60M|                    "We support either two or four chunks per 64-byte block.");
  225|  1.60M|      if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  226|  1.60M|        this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
  227|  1.60M|        this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  228|       |      } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  229|       |        this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
  230|       |        this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  231|       |        this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  232|       |        this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  233|       |      }
  234|  1.60M|      this->prev_incomplete =
  235|  1.60M|          is_incomplete(input.chunks[simd8x64<uint8_t>::NUM_CHUNKS - 1]);
  236|  1.60M|      this->prev_input_block = input.chunks[simd8x64<uint8_t>::NUM_CHUNKS - 1];
  237|  1.60M|      return false;
  238|  1.60M|    }
  239|  1.96M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation12utf8_checker16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  195|  3.21M|                                              const simd8<uint8_t> prev_input) {
  196|       |    // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+
  197|       |    // lead bytes (2, 3, 4-byte leads become large positive numbers instead of
  198|       |    // small negative numbers)
  199|  3.21M|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  200|  3.21M|    simd8<uint8_t> sc = check_special_cases(input, prev1);
  201|  3.21M|    this->error |= check_multibyte_lengths(input, prev_input, sc);
  202|  3.21M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation19check_special_casesENS1_4simd5simd8IhEES5_:
    9|  3.21M|check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
   10|       |  // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
   11|       |  // Bit 1 = Too Long (ASCII followed by continuation)
   12|       |  // Bit 2 = Overlong 3-byte
   13|       |  // Bit 4 = Surrogate
   14|       |  // Bit 5 = Overlong 2-byte
   15|       |  // Bit 7 = Two Continuations
   16|  3.21M|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   17|       |                                               // 11______ 11______
   18|  3.21M|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   19|  3.21M|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   20|  3.21M|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   21|  3.21M|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   22|  3.21M|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   23|  3.21M|  constexpr const uint8_t TOO_LARGE = 1 << 3;  // 11110100 1001____
   24|       |                                               // 11110100 101_____
   25|       |                                               // 11110101 1001____
   26|       |                                               // 11110101 101_____
   27|       |                                               // 1111011_ 1001____
   28|       |                                               // 1111011_ 101_____
   29|       |                                               // 11111___ 1001____
   30|       |                                               // 11111___ 101_____
   31|  3.21M|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   32|       |  // 11110101 1000____
   33|       |  // 1111011_ 1000____
   34|       |  // 11111___ 1000____
   35|  3.21M|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   36|       |
   37|  3.21M|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   38|       |      // 0_______ ________ <ASCII in byte 1>
   39|  3.21M|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   40|  3.21M|      TOO_LONG,
   41|       |      // 10______ ________ <continuation in byte 1>
   42|  3.21M|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   43|       |      // 1100____ ________ <two byte lead in byte 1>
   44|  3.21M|      TOO_SHORT | OVERLONG_2,
   45|       |      // 1101____ ________ <two byte lead in byte 1>
   46|  3.21M|      TOO_SHORT,
   47|       |      // 1110____ ________ <three byte lead in byte 1>
   48|  3.21M|      TOO_SHORT | OVERLONG_3 | SURROGATE,
   49|       |      // 1111____ ________ <four+ byte lead in byte 1>
   50|  3.21M|      TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4);
   51|  3.21M|  constexpr const uint8_t CARRY =
   52|  3.21M|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   53|  3.21M|  const simd8<uint8_t> byte_1_low =
   54|  3.21M|      (prev1 & 0x0F)
   55|  3.21M|          .lookup_16<uint8_t>(
   56|       |              // ____0000 ________
   57|  3.21M|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   58|       |              // ____0001 ________
   59|  3.21M|              CARRY | OVERLONG_2,
   60|       |              // ____001_ ________
   61|  3.21M|              CARRY, CARRY,
   62|       |
   63|       |              // ____0100 ________
   64|  3.21M|              CARRY | TOO_LARGE,
   65|       |              // ____0101 ________
   66|  3.21M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   67|       |              // ____011_ ________
   68|  3.21M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   69|  3.21M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   70|       |
   71|       |              // ____1___ ________
   72|  3.21M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   73|  3.21M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   74|  3.21M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   75|  3.21M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   76|  3.21M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   77|       |              // ____1101 ________
   78|  3.21M|              CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
   79|  3.21M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   80|  3.21M|              CARRY | TOO_LARGE | TOO_LARGE_1000);
   81|  3.21M|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   82|       |      // ________ 0_______ <ASCII in byte 2>
   83|  3.21M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   84|  3.21M|      TOO_SHORT, TOO_SHORT,
   85|       |
   86|       |      // ________ 1000____
   87|  3.21M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   88|  3.21M|          OVERLONG_4,
   89|       |      // ________ 1001____
   90|  3.21M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   91|       |      // ________ 101_____
   92|  3.21M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   93|  3.21M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   94|       |
   95|       |      // ________ 11______
   96|  3.21M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   97|  3.21M|  return (byte_1_high & byte_1_low & byte_2_high);
   98|  3.21M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation23check_multibyte_lengthsENS1_4simd5simd8IhEES5_S5_:
  102|  3.21M|                        const simd8<uint8_t> sc) {
  103|  3.21M|  simd8<uint8_t> prev2 = input.prev<2>(prev_input);
  104|  3.21M|  simd8<uint8_t> prev3 = input.prev<3>(prev_input);
  105|  3.21M|  simd8<uint8_t> must23 =
  106|  3.21M|      simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
  107|  3.21M|  simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
  108|  3.21M|  return must23_80 ^ sc;
  109|  3.21M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation13is_incompleteENS1_4simd5simd8IhEE:
  115|  1.60M|simdutf_really_inline simd8<uint8_t> is_incomplete(const simd8<uint8_t> input) {
  116|       |  // If the previous input's last 3 bytes match this, they're too short (they
  117|       |  // ended at EOF):
  118|       |  // ... 1111____ 111_____ 11______
  119|  1.60M|  static const uint8_t max_array[32] = {255,
  120|  1.60M|                                        255,
  121|  1.60M|                                        255,
  122|  1.60M|                                        255,
  123|  1.60M|                                        255,
  124|  1.60M|                                        255,
  125|  1.60M|                                        255,
  126|  1.60M|                                        255,
  127|  1.60M|                                        255,
  128|  1.60M|                                        255,
  129|  1.60M|                                        255,
  130|  1.60M|                                        255,
  131|  1.60M|                                        255,
  132|  1.60M|                                        255,
  133|  1.60M|                                        255,
  134|  1.60M|                                        255,
  135|  1.60M|                                        255,
  136|  1.60M|                                        255,
  137|  1.60M|                                        255,
  138|  1.60M|                                        255,
  139|  1.60M|                                        255,
  140|  1.60M|                                        255,
  141|  1.60M|                                        255,
  142|  1.60M|                                        255,
  143|  1.60M|                                        255,
  144|  1.60M|                                        255,
  145|  1.60M|                                        255,
  146|  1.60M|                                        255,
  147|  1.60M|                                        255,
  148|  1.60M|                                        0b11110000u - 1,
  149|  1.60M|                                        0b11100000u - 1,
  150|  1.60M|                                        0b11000000u - 1};
  151|  1.60M|  const simd8<uint8_t> max_value(
  152|  1.60M|      &max_array[sizeof(max_array) - sizeof(simd8<uint8_t>)]);
  153|  1.60M|  return input.gt_bits(max_value);
  154|  1.60M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation12utf8_checker9check_eofEv:
  207|  2.48k|  simdutf_really_inline void check_eof() {
  208|       |    // If the previous block had incomplete UTF-8 characters at the end, an
  209|       |    // ASCII block can't possibly finish them.
  210|  2.48k|    this->error |= this->prev_incomplete;
  211|  2.48k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_115utf8_validation12utf8_checker6errorsEv:
  252|  2.48k|  simdutf_really_inline bool errors() const {
  253|  2.48k|    return this->error.any_bits_set_anywhere();
  254|  2.48k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation12utf8_checker23check_overlapping_inputERKNS1_4simd8simd8x64IhEENS4_5simd8IhEE:
  246|    793|                          const simd8<uint8_t> prev_input) {
  247|    793|    this->prev_input_block = prev_input;
  248|    793|    this->check_next_input(input);
  249|    793|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation12utf8_checker16check_next_inputERKNS1_4simd8simd8x64IhEE:
  215|  1.96M|  simdutf_really_inline bool check_next_input(const simd8x64<uint8_t> &input) {
  216|  1.96M|    if (simdutf_likely(is_ascii(input))) {
  ------------------
  |  |   90|  1.96M|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (90:31): [True: 358k, False: 1.60M]
  |  |  ------------------
  ------------------
  217|   358k|      this->error |= this->prev_incomplete;
  218|   358k|      return true;
  219|  1.60M|    } else {
  220|       |      // you might think that a for-loop would work, but under Visual Studio, it
  221|       |      // is not good enough.
  222|  1.60M|      static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  223|  1.60M|                        (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  224|  1.60M|                    "We support either two or four chunks per 64-byte block.");
  225|       |      if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  226|       |        this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
  227|       |        this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  228|  1.60M|      } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  229|  1.60M|        this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
  230|  1.60M|        this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  231|  1.60M|        this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  232|  1.60M|        this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  233|  1.60M|      }
  234|  1.60M|      this->prev_incomplete =
  235|  1.60M|          is_incomplete(input.chunks[simd8x64<uint8_t>::NUM_CHUNKS - 1]);
  236|  1.60M|      this->prev_input_block = input.chunks[simd8x64<uint8_t>::NUM_CHUNKS - 1];
  237|  1.60M|      return false;
  238|  1.60M|    }
  239|  1.96M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation12utf8_checker16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  195|  6.43M|                                              const simd8<uint8_t> prev_input) {
  196|       |    // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+
  197|       |    // lead bytes (2, 3, 4-byte leads become large positive numbers instead of
  198|       |    // small negative numbers)
  199|  6.43M|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  200|  6.43M|    simd8<uint8_t> sc = check_special_cases(input, prev1);
  201|  6.43M|    this->error |= check_multibyte_lengths(input, prev_input, sc);
  202|  6.43M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation19check_special_casesENS1_4simd5simd8IhEES5_:
    9|  6.43M|check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
   10|       |  // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
   11|       |  // Bit 1 = Too Long (ASCII followed by continuation)
   12|       |  // Bit 2 = Overlong 3-byte
   13|       |  // Bit 4 = Surrogate
   14|       |  // Bit 5 = Overlong 2-byte
   15|       |  // Bit 7 = Two Continuations
   16|  6.43M|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   17|       |                                               // 11______ 11______
   18|  6.43M|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   19|  6.43M|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   20|  6.43M|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   21|  6.43M|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   22|  6.43M|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   23|  6.43M|  constexpr const uint8_t TOO_LARGE = 1 << 3;  // 11110100 1001____
   24|       |                                               // 11110100 101_____
   25|       |                                               // 11110101 1001____
   26|       |                                               // 11110101 101_____
   27|       |                                               // 1111011_ 1001____
   28|       |                                               // 1111011_ 101_____
   29|       |                                               // 11111___ 1001____
   30|       |                                               // 11111___ 101_____
   31|  6.43M|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   32|       |  // 11110101 1000____
   33|       |  // 1111011_ 1000____
   34|       |  // 11111___ 1000____
   35|  6.43M|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   36|       |
   37|  6.43M|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   38|       |      // 0_______ ________ <ASCII in byte 1>
   39|  6.43M|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   40|  6.43M|      TOO_LONG,
   41|       |      // 10______ ________ <continuation in byte 1>
   42|  6.43M|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   43|       |      // 1100____ ________ <two byte lead in byte 1>
   44|  6.43M|      TOO_SHORT | OVERLONG_2,
   45|       |      // 1101____ ________ <two byte lead in byte 1>
   46|  6.43M|      TOO_SHORT,
   47|       |      // 1110____ ________ <three byte lead in byte 1>
   48|  6.43M|      TOO_SHORT | OVERLONG_3 | SURROGATE,
   49|       |      // 1111____ ________ <four+ byte lead in byte 1>
   50|  6.43M|      TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4);
   51|  6.43M|  constexpr const uint8_t CARRY =
   52|  6.43M|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   53|  6.43M|  const simd8<uint8_t> byte_1_low =
   54|  6.43M|      (prev1 & 0x0F)
   55|  6.43M|          .lookup_16<uint8_t>(
   56|       |              // ____0000 ________
   57|  6.43M|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   58|       |              // ____0001 ________
   59|  6.43M|              CARRY | OVERLONG_2,
   60|       |              // ____001_ ________
   61|  6.43M|              CARRY, CARRY,
   62|       |
   63|       |              // ____0100 ________
   64|  6.43M|              CARRY | TOO_LARGE,
   65|       |              // ____0101 ________
   66|  6.43M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   67|       |              // ____011_ ________
   68|  6.43M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   69|  6.43M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   70|       |
   71|       |              // ____1___ ________
   72|  6.43M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   73|  6.43M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   74|  6.43M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   75|  6.43M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   76|  6.43M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   77|       |              // ____1101 ________
   78|  6.43M|              CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
   79|  6.43M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   80|  6.43M|              CARRY | TOO_LARGE | TOO_LARGE_1000);
   81|  6.43M|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   82|       |      // ________ 0_______ <ASCII in byte 2>
   83|  6.43M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   84|  6.43M|      TOO_SHORT, TOO_SHORT,
   85|       |
   86|       |      // ________ 1000____
   87|  6.43M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   88|  6.43M|          OVERLONG_4,
   89|       |      // ________ 1001____
   90|  6.43M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   91|       |      // ________ 101_____
   92|  6.43M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   93|  6.43M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   94|       |
   95|       |      // ________ 11______
   96|  6.43M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   97|  6.43M|  return (byte_1_high & byte_1_low & byte_2_high);
   98|  6.43M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation23check_multibyte_lengthsENS1_4simd5simd8IhEES5_S5_:
  102|  6.43M|                        const simd8<uint8_t> sc) {
  103|  6.43M|  simd8<uint8_t> prev2 = input.prev<2>(prev_input);
  104|  6.43M|  simd8<uint8_t> prev3 = input.prev<3>(prev_input);
  105|  6.43M|  simd8<uint8_t> must23 =
  106|  6.43M|      simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
  107|  6.43M|  simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
  108|  6.43M|  return must23_80 ^ sc;
  109|  6.43M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation13is_incompleteENS1_4simd5simd8IhEE:
  115|  1.60M|simdutf_really_inline simd8<uint8_t> is_incomplete(const simd8<uint8_t> input) {
  116|       |  // If the previous input's last 3 bytes match this, they're too short (they
  117|       |  // ended at EOF):
  118|       |  // ... 1111____ 111_____ 11______
  119|  1.60M|  static const uint8_t max_array[32] = {255,
  120|  1.60M|                                        255,
  121|  1.60M|                                        255,
  122|  1.60M|                                        255,
  123|  1.60M|                                        255,
  124|  1.60M|                                        255,
  125|  1.60M|                                        255,
  126|  1.60M|                                        255,
  127|  1.60M|                                        255,
  128|  1.60M|                                        255,
  129|  1.60M|                                        255,
  130|  1.60M|                                        255,
  131|  1.60M|                                        255,
  132|  1.60M|                                        255,
  133|  1.60M|                                        255,
  134|  1.60M|                                        255,
  135|  1.60M|                                        255,
  136|  1.60M|                                        255,
  137|  1.60M|                                        255,
  138|  1.60M|                                        255,
  139|  1.60M|                                        255,
  140|  1.60M|                                        255,
  141|  1.60M|                                        255,
  142|  1.60M|                                        255,
  143|  1.60M|                                        255,
  144|  1.60M|                                        255,
  145|  1.60M|                                        255,
  146|  1.60M|                                        255,
  147|  1.60M|                                        255,
  148|  1.60M|                                        0b11110000u - 1,
  149|  1.60M|                                        0b11100000u - 1,
  150|  1.60M|                                        0b11000000u - 1};
  151|  1.60M|  const simd8<uint8_t> max_value(
  152|  1.60M|      &max_array[sizeof(max_array) - sizeof(simd8<uint8_t>)]);
  153|  1.60M|  return input.gt_bits(max_value);
  154|  1.60M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation12utf8_checker9check_eofEv:
  207|  2.48k|  simdutf_really_inline void check_eof() {
  208|       |    // If the previous block had incomplete UTF-8 characters at the end, an
  209|       |    // ASCII block can't possibly finish them.
  210|  2.48k|    this->error |= this->prev_incomplete;
  211|  2.48k|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_115utf8_validation12utf8_checker6errorsEv:
  252|  2.48k|  simdutf_really_inline bool errors() const {
  253|  2.48k|    return this->error.any_bits_set_anywhere();
  254|  2.48k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation12utf8_checker23check_overlapping_inputERKNS1_4simd8simd8x64IhEENS4_5simd8IhEE:
  246|    865|                          const simd8<uint8_t> prev_input) {
  247|    865|    this->prev_input_block = prev_input;
  248|    865|    this->check_next_input(input);
  249|    865|  }

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation21generic_validate_utf8EPKcm:
   58|  2.48k|bool generic_validate_utf8(const char *input, size_t length) {
   59|  2.48k|  return generic_validate_utf8<utf8_checker>(
   60|  2.48k|      reinterpret_cast<const uint8_t *>(input), length);
   61|  2.48k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation21generic_validate_utf8INS2_12utf8_checkerEEEbPKhm:
   46|  2.48k|bool generic_validate_utf8(const uint8_t *input, size_t length) {
   47|  2.48k|  checker c{};
   48|  2.48k|  buf_block_reader<64> reader(input, length);
   49|  1.96M|  while (reader.has_full_block()) {
  ------------------
  |  Branch (49:10): [True: 1.96M, False: 2.48k]
  ------------------
   50|  1.96M|    simd::simd8x64<uint8_t> in(reader.full_block());
   51|  1.96M|    c.check_next_input(in);
   52|  1.96M|    reader.advance();
   53|  1.96M|  }
   54|  2.48k|  check_remainder(c, input, length, reader.block_index());
   55|  2.48k|  return !c.errors();
   56|  2.48k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation15check_remainderINS2_12utf8_checkerEEEvRT_PKhmm:
   15|  2.48k|                                           size_t length, size_t idx) {
   16|  2.48k|  constexpr size_t block = sizeof(simd::simd8x64<uint8_t>);
   17|  2.48k|  constexpr size_t width = sizeof(simd::simd8<uint8_t>);
   18|  2.48k|  static_assert(block == 8 * sizeof(uint64_t), "one mask bit per byte");
   19|  2.48k|  if (length >= block + width) {
  ------------------
  |  Branch (19:7): [True: 913, False: 1.56k]
  ------------------
   20|       |    // The window starts at or before idx: buf_block_reader leaves 1 to 64
   21|       |    // bytes, so 0 <= idx - start < 64.
   22|    913|    const size_t start = length - block;
   23|    913|    const simd::simd8x64<uint8_t> in(input + start);
   24|       |    // Bit i is set if byte start + i is not ASCII, so the bytes from idx on
   25|       |    // are the top length - idx bits. is_ascii() goes first because GCC, up
   26|       |    // to at least GCC 17, builds gteq_unsigned(0x80) from min and compare
   27|       |    // instead of one movemask, which costs ASCII tails about 0.5 ns.
   28|    913|    if (!in.is_ascii() && (in.gteq_unsigned(0x80) >> (idx - start)) != 0) {
  ------------------
  |  Branch (28:9): [True: 815, False: 98]
  |  Branch (28:27): [True: 793, False: 22]
  ------------------
   29|    793|      c.check_overlapping_input(in,
   30|    793|                                simd::simd8<uint8_t>(input + start - width));
   31|    793|    }
   32|  1.56k|  } else if (idx != length) {
  ------------------
  |  Branch (32:14): [True: 1.56k, False: 4]
  ------------------
   33|       |    // Only an empty input leaves no bytes. Its pointer may be null, and
   34|       |    // memcpy from a null pointer is undefined.
   35|  1.56k|    uint8_t buffer[block]{};
   36|  1.56k|    std::memcpy(buffer, input + idx, length - idx);
   37|  1.56k|    c.check_next_input(simd::simd8x64<uint8_t>(buffer));
   38|  1.56k|  }
   39|  2.48k|  c.check_eof();
   40|  2.48k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation21generic_validate_utf8EPKcm:
   58|  2.48k|bool generic_validate_utf8(const char *input, size_t length) {
   59|  2.48k|  return generic_validate_utf8<utf8_checker>(
   60|  2.48k|      reinterpret_cast<const uint8_t *>(input), length);
   61|  2.48k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation21generic_validate_utf8INS2_12utf8_checkerEEEbPKhm:
   46|  2.48k|bool generic_validate_utf8(const uint8_t *input, size_t length) {
   47|  2.48k|  checker c{};
   48|  2.48k|  buf_block_reader<64> reader(input, length);
   49|  1.96M|  while (reader.has_full_block()) {
  ------------------
  |  Branch (49:10): [True: 1.96M, False: 2.48k]
  ------------------
   50|  1.96M|    simd::simd8x64<uint8_t> in(reader.full_block());
   51|  1.96M|    c.check_next_input(in);
   52|  1.96M|    reader.advance();
   53|  1.96M|  }
   54|  2.48k|  check_remainder(c, input, length, reader.block_index());
   55|  2.48k|  return !c.errors();
   56|  2.48k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation15check_remainderINS2_12utf8_checkerEEEvRT_PKhmm:
   15|  2.48k|                                           size_t length, size_t idx) {
   16|  2.48k|  constexpr size_t block = sizeof(simd::simd8x64<uint8_t>);
   17|  2.48k|  constexpr size_t width = sizeof(simd::simd8<uint8_t>);
   18|  2.48k|  static_assert(block == 8 * sizeof(uint64_t), "one mask bit per byte");
   19|  2.48k|  if (length >= block + width) {
  ------------------
  |  Branch (19:7): [True: 988, False: 1.49k]
  ------------------
   20|       |    // The window starts at or before idx: buf_block_reader leaves 1 to 64
   21|       |    // bytes, so 0 <= idx - start < 64.
   22|    988|    const size_t start = length - block;
   23|    988|    const simd::simd8x64<uint8_t> in(input + start);
   24|       |    // Bit i is set if byte start + i is not ASCII, so the bytes from idx on
   25|       |    // are the top length - idx bits. is_ascii() goes first because GCC, up
   26|       |    // to at least GCC 17, builds gteq_unsigned(0x80) from min and compare
   27|       |    // instead of one movemask, which costs ASCII tails about 0.5 ns.
   28|    988|    if (!in.is_ascii() && (in.gteq_unsigned(0x80) >> (idx - start)) != 0) {
  ------------------
  |  Branch (28:9): [True: 889, False: 99]
  |  Branch (28:27): [True: 865, False: 24]
  ------------------
   29|    865|      c.check_overlapping_input(in,
   30|    865|                                simd::simd8<uint8_t>(input + start - width));
   31|    865|    }
   32|  1.49k|  } else if (idx != length) {
  ------------------
  |  Branch (32:14): [True: 1.49k, False: 4]
  ------------------
   33|       |    // Only an empty input leaves no bytes. Its pointer may be null, and
   34|       |    // memcpy from a null pointer is undefined.
   35|  1.49k|    uint8_t buffer[block]{};
   36|  1.49k|    std::memcpy(buffer, input + idx, length - idx);
   37|  1.49k|    c.check_next_input(simd::simd8x64<uint8_t>(buffer));
   38|  1.49k|  }
   39|  2.48k|  c.check_eof();
   40|  2.48k|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_138avx2_convert_utf16_to_utf8_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
  333|  2.46M|                                       char *utf8_output) {
  334|  2.46M|  const char16_t *start = buf;
  335|  2.46M|  const char16_t *end = buf + len;
  336|       |
  337|  2.46M|  const __m256i v_0000 = _mm256_setzero_si256();
  338|  2.46M|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
  339|  2.46M|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
  340|  2.46M|  const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
  341|  2.46M|  const size_t safety_margin =
  342|  2.46M|      12; // to avoid overruns, see issue
  343|       |          // https://github.com/simdutf/simdutf/issues/92
  344|       |
  345|  4.31M|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (345:10): [True: 4.31M, False: 4.19k]
  ------------------
  346|  4.31M|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
  347|  4.31M|    if (big_endian) {
  ------------------
  |  Branch (347:9): [Folded, False: 4.31M]
  ------------------
  348|      0|      const __m256i swap = _mm256_setr_epi8(
  349|      0|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
  350|      0|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
  351|      0|      in = _mm256_shuffle_epi8(in, swap);
  352|      0|    }
  353|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
  354|  4.31M|    const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
  355|  4.31M|    if (_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (355:9): [True: 758k, False: 3.55M]
  ------------------
  356|       |      // 1. pack the bytes
  357|   758k|      const __m128i utf8_packed = _mm_packus_epi16(
  358|   758k|          _mm256_castsi256_si128(in), _mm256_extractf128_si256(in, 1));
  359|       |      // 2. store (16 bytes)
  360|   758k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  361|       |      // 3. adjust pointers
  362|   758k|      buf += 16;
  363|   758k|      utf8_output += 16;
  364|   758k|      continue; // we are done for this round!
  365|   758k|    }
  366|       |    // no bits set above 7th bit
  367|  3.55M|    const __m256i one_byte_bytemask =
  368|  3.55M|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
  369|  3.55M|    const uint32_t one_byte_bitmask =
  370|  3.55M|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
  371|       |
  372|       |    // no bits set above 11th bit
  373|  3.55M|    const __m256i one_or_two_bytes_bytemask =
  374|  3.55M|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000);
  375|  3.55M|    const uint32_t one_or_two_bytes_bitmask =
  376|  3.55M|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
  377|  3.55M|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (377:9): [True: 14.3k, False: 3.54M]
  ------------------
  378|       |
  379|       |      // 1. prepare 2-byte values
  380|       |      // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
  381|       |      // expected output   : [110a|aaaa|10bb|bbbb] x 8
  382|  14.3k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
  383|  14.3k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
  384|       |
  385|       |      // t0 = [000a|aaaa|bbbb|bb00]
  386|  14.3k|      const __m256i t0 = _mm256_slli_epi16(in, 2);
  387|       |      // t1 = [000a|aaaa|0000|0000]
  388|  14.3k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
  389|       |      // t2 = [0000|0000|00bb|bbbb]
  390|  14.3k|      const __m256i t2 = _mm256_and_si256(in, v_003f);
  391|       |      // t3 = [000a|aaaa|00bb|bbbb]
  392|  14.3k|      const __m256i t3 = _mm256_or_si256(t1, t2);
  393|       |      // t4 = [110a|aaaa|10bb|bbbb]
  394|  14.3k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
  395|       |
  396|       |      // 2. merge ASCII and 2-byte codewords
  397|  14.3k|      const __m256i utf8_unpacked =
  398|  14.3k|          _mm256_blendv_epi8(t4, in, one_byte_bytemask);
  399|       |
  400|       |      // 3. prepare bitmask for 8-bit lookup
  401|  14.3k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
  402|  14.3k|      const uint32_t M1 = M0 >> 7;
  403|  14.3k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
  404|       |      // 4. pack the bytes
  405|       |
  406|  14.3k|      const uint8_t *row =
  407|  14.3k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
  408|  14.3k|      const uint8_t *row_2 =
  409|  14.3k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
  410|  14.3k|                                                                       16)][0];
  411|       |
  412|  14.3k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  413|  14.3k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
  414|       |
  415|  14.3k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
  416|  14.3k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|  14.3k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|  14.3k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  417|       |      // 5. store bytes
  418|  14.3k|      _mm_storeu_si128((__m128i *)utf8_output,
  419|  14.3k|                       _mm256_castsi256_si128(utf8_packed));
  420|  14.3k|      utf8_output += row[0];
  421|  14.3k|      _mm_storeu_si128((__m128i *)utf8_output,
  422|  14.3k|                       _mm256_extractf128_si256(utf8_packed, 1));
  423|  14.3k|      utf8_output += row_2[0];
  424|       |
  425|       |      // 6. adjust pointers
  426|  14.3k|      buf += 16;
  427|  14.3k|      continue;
  428|  14.3k|    }
  429|       |    // 1. Check if there are any surrogate word in the input chunk.
  430|       |    //    We have also deal with situation when there is a surrogate word
  431|       |    //    at the end of a chunk.
  432|  3.54M|    const __m256i surrogates_bytemask =
  433|  3.54M|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800);
  434|       |
  435|       |    // bitmask = 0x0000 if there are no surrogates
  436|       |    //         = 0xc000 if the last word is a surrogate
  437|  3.54M|    const uint32_t surrogates_bitmask =
  438|  3.54M|        static_cast<uint32_t>(_mm256_movemask_epi8(surrogates_bytemask));
  439|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
  440|       |    // However, it is likely an uncommon occurrence.
  441|  3.54M|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (441:9): [True: 1.07M, False: 2.47M]
  ------------------
  442|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  443|  1.07M|      const __m256i dup_even = _mm256_setr_epi16(
  444|  1.07M|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  445|  1.07M|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
  446|       |
  447|       |      /* In this branch we handle three cases:
  448|       |         1. [0000|0000|0ccc|cccc] => [0ccc|cccc]                           -
  449|       |        single UFT-8 byte
  450|       |         2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc]              - two
  451|       |        UTF-8 bytes
  452|       |         3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] -
  453|       |        three UTF-8 bytes
  454|       |
  455|       |        We expand the input word (16-bit) into two code units (32-bit), thus
  456|       |        we have room for four bytes. However, we need five distinct bit
  457|       |        layouts. Note that the last byte in cases #2 and #3 is the same.
  458|       |
  459|       |        We precompute byte 1 for case #1 and the common byte for cases #2 & #3
  460|       |        in register t2.
  461|       |
  462|       |        We precompute byte 1 for case #3 and -- **conditionally** -- precompute
  463|       |        either byte 1 for case #2 or byte 2 for case #3. Note that they
  464|       |        differ by exactly one bit.
  465|       |
  466|       |        Finally from these two code units we build proper UTF-8 sequence, taking
  467|       |        into account the case (i.e, the number of bytes to write).
  468|       |      */
  469|       |      /**
  470|       |       * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
  471|       |       * t2 => [0ccc|cccc] [10cc|cccc]
  472|       |       * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
  473|       |       */
  474|  1.07M|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  475|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  476|  1.07M|      const __m256i t0 = _mm256_shuffle_epi8(in, dup_even);
  477|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  478|  1.07M|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  474|  1.07M|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  479|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  480|  1.07M|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  474|  1.07M|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  481|       |
  482|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  483|  1.07M|      const __m256i s0 = _mm256_srli_epi16(in, 4);
  484|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  485|  1.07M|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  474|  1.07M|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  486|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  487|  1.07M|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  474|  1.07M|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  488|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  489|  1.07M|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  474|  1.07M|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  490|  1.07M|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  491|  1.07M|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  474|  1.07M|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  492|  1.07M|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  493|  1.07M|#undef simdutf_vec
  494|       |
  495|       |      // 4. expand code units 16-bit => 32-bit
  496|  1.07M|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  497|  1.07M|      const __m256i out1 = _mm256_unpackhi_epi16(t2, s4);
  498|       |
  499|       |      // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
  500|  1.07M|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  501|  1.07M|                            (one_or_two_bytes_bitmask & 0xaaaaaaaa);
  502|       |      // Due to the wider registers, the following path is less likely to be
  503|       |      // useful.
  504|       |      /*if(mask == 0) {
  505|       |        // We only have three-byte code units. Use fast path.
  506|       |        const __m256i shuffle =
  507|       |      _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1,
  508|       |      2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); const __m256i utf8_0 =
  509|       |      _mm256_shuffle_epi8(out0, shuffle); const __m256i utf8_1 =
  510|       |      _mm256_shuffle_epi8(out1, shuffle);
  511|       |        _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0));
  512|       |        utf8_output += 12;
  513|       |        _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1));
  514|       |        utf8_output += 12;
  515|       |        _mm_storeu_si128((__m128i*)utf8_output,
  516|       |      _mm256_extractf128_si256(utf8_0,1)); utf8_output += 12;
  517|       |        _mm_storeu_si128((__m128i*)utf8_output,
  518|       |      _mm256_extractf128_si256(utf8_1,1)); utf8_output += 12; buf += 16;
  519|       |        continue;
  520|       |      }*/
  521|  1.07M|      const uint8_t mask0 = uint8_t(mask);
  522|  1.07M|      const uint8_t *row0 =
  523|  1.07M|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  524|  1.07M|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  525|  1.07M|      const __m128i utf8_0 =
  526|  1.07M|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  527|       |
  528|  1.07M|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  529|  1.07M|      const uint8_t *row1 =
  530|  1.07M|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  531|  1.07M|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  532|  1.07M|      const __m128i utf8_1 =
  533|  1.07M|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  534|       |
  535|  1.07M|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  536|  1.07M|      const uint8_t *row2 =
  537|  1.07M|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  538|  1.07M|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  539|  1.07M|      const __m128i utf8_2 =
  540|  1.07M|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  541|       |
  542|  1.07M|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  543|  1.07M|      const uint8_t *row3 =
  544|  1.07M|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  545|  1.07M|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  546|  1.07M|      const __m128i utf8_3 =
  547|  1.07M|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  548|       |
  549|  1.07M|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  550|  1.07M|      utf8_output += row0[0];
  551|  1.07M|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  552|  1.07M|      utf8_output += row1[0];
  553|  1.07M|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  554|  1.07M|      utf8_output += row2[0];
  555|  1.07M|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  556|  1.07M|      utf8_output += row3[0];
  557|  1.07M|      buf += 16;
  558|       |      // surrogate pair(s) in a register
  559|  2.47M|    } else {
  560|       |      // Let us do a scalar fallback.
  561|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
  562|       |      // in the presence of surrogate pairs may require non-trivial tables.
  563|  2.47M|      size_t forward = 15;
  564|  2.47M|      size_t k = 0;
  565|  2.47M|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (565:11): [True: 0, False: 2.47M]
  ------------------
  566|      0|        forward = size_t(end - buf - 1);
  567|      0|      }
  568|  3.26M|      for (; k < forward; k++) {
  ------------------
  |  Branch (568:14): [True: 3.25M, False: 6.57k]
  ------------------
  569|  3.25M|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  570|  3.25M|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (570:13): [True: 47.2k, False: 3.20M]
  ------------------
  571|  47.2k|          *utf8_output++ = char(word);
  572|  3.20M|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (572:20): [True: 30.5k, False: 3.17M]
  ------------------
  573|  30.5k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  574|  30.5k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  575|  3.17M|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (575:20): [True: 681k, False: 2.49M]
  ------------------
  576|   681k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  577|   681k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  578|   681k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  579|  2.49M|        } else {
  580|       |          // must be a surrogate pair
  581|  2.49M|          uint16_t diff = uint16_t(word - 0xD800);
  582|  2.49M|          uint16_t next_word =
  583|  2.49M|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  584|  2.49M|          k++;
  585|  2.49M|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  586|  2.49M|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (586:15): [True: 2.46M, False: 31.2k]
  ------------------
  587|  2.46M|            return std::make_pair(
  588|  2.46M|                result(error_code::SURROGATE, buf - start + k - 1),
  589|  2.46M|                utf8_output);
  590|  2.46M|          }
  591|  31.2k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  592|  31.2k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  593|  31.2k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  594|  31.2k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  595|  31.2k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  596|  31.2k|        }
  597|  3.25M|      }
  598|  6.57k|      buf += k;
  599|  6.57k|    }
  600|  3.54M|  } // while
  601|  4.19k|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  602|  2.46M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_138avx2_convert_utf16_to_utf8_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
  333|  2.11M|                                       char *utf8_output) {
  334|  2.11M|  const char16_t *start = buf;
  335|  2.11M|  const char16_t *end = buf + len;
  336|       |
  337|  2.11M|  const __m256i v_0000 = _mm256_setzero_si256();
  338|  2.11M|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
  339|  2.11M|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
  340|  2.11M|  const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
  341|  2.11M|  const size_t safety_margin =
  342|  2.11M|      12; // to avoid overruns, see issue
  343|       |          // https://github.com/simdutf/simdutf/issues/92
  344|       |
  345|  3.54M|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (345:10): [True: 3.54M, False: 4.37k]
  ------------------
  346|  3.54M|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
  347|  3.54M|    if (big_endian) {
  ------------------
  |  Branch (347:9): [True: 3.54M, Folded]
  ------------------
  348|  3.54M|      const __m256i swap = _mm256_setr_epi8(
  349|  3.54M|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
  350|  3.54M|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
  351|  3.54M|      in = _mm256_shuffle_epi8(in, swap);
  352|  3.54M|    }
  353|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
  354|  3.54M|    const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
  355|  3.54M|    if (_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (355:9): [True: 712k, False: 2.83M]
  ------------------
  356|       |      // 1. pack the bytes
  357|   712k|      const __m128i utf8_packed = _mm_packus_epi16(
  358|   712k|          _mm256_castsi256_si128(in), _mm256_extractf128_si256(in, 1));
  359|       |      // 2. store (16 bytes)
  360|   712k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  361|       |      // 3. adjust pointers
  362|   712k|      buf += 16;
  363|   712k|      utf8_output += 16;
  364|   712k|      continue; // we are done for this round!
  365|   712k|    }
  366|       |    // no bits set above 7th bit
  367|  2.83M|    const __m256i one_byte_bytemask =
  368|  2.83M|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
  369|  2.83M|    const uint32_t one_byte_bitmask =
  370|  2.83M|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
  371|       |
  372|       |    // no bits set above 11th bit
  373|  2.83M|    const __m256i one_or_two_bytes_bytemask =
  374|  2.83M|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000);
  375|  2.83M|    const uint32_t one_or_two_bytes_bitmask =
  376|  2.83M|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
  377|  2.83M|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (377:9): [True: 12.2k, False: 2.81M]
  ------------------
  378|       |
  379|       |      // 1. prepare 2-byte values
  380|       |      // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
  381|       |      // expected output   : [110a|aaaa|10bb|bbbb] x 8
  382|  12.2k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
  383|  12.2k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
  384|       |
  385|       |      // t0 = [000a|aaaa|bbbb|bb00]
  386|  12.2k|      const __m256i t0 = _mm256_slli_epi16(in, 2);
  387|       |      // t1 = [000a|aaaa|0000|0000]
  388|  12.2k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
  389|       |      // t2 = [0000|0000|00bb|bbbb]
  390|  12.2k|      const __m256i t2 = _mm256_and_si256(in, v_003f);
  391|       |      // t3 = [000a|aaaa|00bb|bbbb]
  392|  12.2k|      const __m256i t3 = _mm256_or_si256(t1, t2);
  393|       |      // t4 = [110a|aaaa|10bb|bbbb]
  394|  12.2k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
  395|       |
  396|       |      // 2. merge ASCII and 2-byte codewords
  397|  12.2k|      const __m256i utf8_unpacked =
  398|  12.2k|          _mm256_blendv_epi8(t4, in, one_byte_bytemask);
  399|       |
  400|       |      // 3. prepare bitmask for 8-bit lookup
  401|  12.2k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
  402|  12.2k|      const uint32_t M1 = M0 >> 7;
  403|  12.2k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
  404|       |      // 4. pack the bytes
  405|       |
  406|  12.2k|      const uint8_t *row =
  407|  12.2k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
  408|  12.2k|      const uint8_t *row_2 =
  409|  12.2k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
  410|  12.2k|                                                                       16)][0];
  411|       |
  412|  12.2k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  413|  12.2k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
  414|       |
  415|  12.2k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
  416|  12.2k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|  12.2k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|  12.2k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  417|       |      // 5. store bytes
  418|  12.2k|      _mm_storeu_si128((__m128i *)utf8_output,
  419|  12.2k|                       _mm256_castsi256_si128(utf8_packed));
  420|  12.2k|      utf8_output += row[0];
  421|  12.2k|      _mm_storeu_si128((__m128i *)utf8_output,
  422|  12.2k|                       _mm256_extractf128_si256(utf8_packed, 1));
  423|  12.2k|      utf8_output += row_2[0];
  424|       |
  425|       |      // 6. adjust pointers
  426|  12.2k|      buf += 16;
  427|  12.2k|      continue;
  428|  12.2k|    }
  429|       |    // 1. Check if there are any surrogate word in the input chunk.
  430|       |    //    We have also deal with situation when there is a surrogate word
  431|       |    //    at the end of a chunk.
  432|  2.81M|    const __m256i surrogates_bytemask =
  433|  2.81M|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800);
  434|       |
  435|       |    // bitmask = 0x0000 if there are no surrogates
  436|       |    //         = 0xc000 if the last word is a surrogate
  437|  2.81M|    const uint32_t surrogates_bitmask =
  438|  2.81M|        static_cast<uint32_t>(_mm256_movemask_epi8(surrogates_bytemask));
  439|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
  440|       |    // However, it is likely an uncommon occurrence.
  441|  2.81M|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (441:9): [True: 695k, False: 2.12M]
  ------------------
  442|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  443|   695k|      const __m256i dup_even = _mm256_setr_epi16(
  444|   695k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  445|   695k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
  446|       |
  447|       |      /* In this branch we handle three cases:
  448|       |         1. [0000|0000|0ccc|cccc] => [0ccc|cccc]                           -
  449|       |        single UFT-8 byte
  450|       |         2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc]              - two
  451|       |        UTF-8 bytes
  452|       |         3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] -
  453|       |        three UTF-8 bytes
  454|       |
  455|       |        We expand the input word (16-bit) into two code units (32-bit), thus
  456|       |        we have room for four bytes. However, we need five distinct bit
  457|       |        layouts. Note that the last byte in cases #2 and #3 is the same.
  458|       |
  459|       |        We precompute byte 1 for case #1 and the common byte for cases #2 & #3
  460|       |        in register t2.
  461|       |
  462|       |        We precompute byte 1 for case #3 and -- **conditionally** -- precompute
  463|       |        either byte 1 for case #2 or byte 2 for case #3. Note that they
  464|       |        differ by exactly one bit.
  465|       |
  466|       |        Finally from these two code units we build proper UTF-8 sequence, taking
  467|       |        into account the case (i.e, the number of bytes to write).
  468|       |      */
  469|       |      /**
  470|       |       * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
  471|       |       * t2 => [0ccc|cccc] [10cc|cccc]
  472|       |       * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
  473|       |       */
  474|   695k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  475|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  476|   695k|      const __m256i t0 = _mm256_shuffle_epi8(in, dup_even);
  477|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  478|   695k|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  474|   695k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  479|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  480|   695k|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  474|   695k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  481|       |
  482|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  483|   695k|      const __m256i s0 = _mm256_srli_epi16(in, 4);
  484|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  485|   695k|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  474|   695k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  486|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  487|   695k|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  474|   695k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  488|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  489|   695k|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  474|   695k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  490|   695k|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  491|   695k|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  474|   695k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  492|   695k|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  493|   695k|#undef simdutf_vec
  494|       |
  495|       |      // 4. expand code units 16-bit => 32-bit
  496|   695k|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  497|   695k|      const __m256i out1 = _mm256_unpackhi_epi16(t2, s4);
  498|       |
  499|       |      // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
  500|   695k|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  501|   695k|                            (one_or_two_bytes_bitmask & 0xaaaaaaaa);
  502|       |      // Due to the wider registers, the following path is less likely to be
  503|       |      // useful.
  504|       |      /*if(mask == 0) {
  505|       |        // We only have three-byte code units. Use fast path.
  506|       |        const __m256i shuffle =
  507|       |      _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1,
  508|       |      2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); const __m256i utf8_0 =
  509|       |      _mm256_shuffle_epi8(out0, shuffle); const __m256i utf8_1 =
  510|       |      _mm256_shuffle_epi8(out1, shuffle);
  511|       |        _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0));
  512|       |        utf8_output += 12;
  513|       |        _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1));
  514|       |        utf8_output += 12;
  515|       |        _mm_storeu_si128((__m128i*)utf8_output,
  516|       |      _mm256_extractf128_si256(utf8_0,1)); utf8_output += 12;
  517|       |        _mm_storeu_si128((__m128i*)utf8_output,
  518|       |      _mm256_extractf128_si256(utf8_1,1)); utf8_output += 12; buf += 16;
  519|       |        continue;
  520|       |      }*/
  521|   695k|      const uint8_t mask0 = uint8_t(mask);
  522|   695k|      const uint8_t *row0 =
  523|   695k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  524|   695k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  525|   695k|      const __m128i utf8_0 =
  526|   695k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  527|       |
  528|   695k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  529|   695k|      const uint8_t *row1 =
  530|   695k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  531|   695k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  532|   695k|      const __m128i utf8_1 =
  533|   695k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  534|       |
  535|   695k|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  536|   695k|      const uint8_t *row2 =
  537|   695k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  538|   695k|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  539|   695k|      const __m128i utf8_2 =
  540|   695k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  541|       |
  542|   695k|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  543|   695k|      const uint8_t *row3 =
  544|   695k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  545|   695k|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  546|   695k|      const __m128i utf8_3 =
  547|   695k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  548|       |
  549|   695k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  550|   695k|      utf8_output += row0[0];
  551|   695k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  552|   695k|      utf8_output += row1[0];
  553|   695k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  554|   695k|      utf8_output += row2[0];
  555|   695k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  556|   695k|      utf8_output += row3[0];
  557|   695k|      buf += 16;
  558|       |      // surrogate pair(s) in a register
  559|  2.12M|    } else {
  560|       |      // Let us do a scalar fallback.
  561|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
  562|       |      // in the presence of surrogate pairs may require non-trivial tables.
  563|  2.12M|      size_t forward = 15;
  564|  2.12M|      size_t k = 0;
  565|  2.12M|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (565:11): [True: 0, False: 2.12M]
  ------------------
  566|      0|        forward = size_t(end - buf - 1);
  567|      0|      }
  568|  3.33M|      for (; k < forward; k++) {
  ------------------
  |  Branch (568:14): [True: 3.32M, False: 8.44k]
  ------------------
  569|  3.32M|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  570|  3.32M|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (570:13): [True: 83.0k, False: 3.24M]
  ------------------
  571|  83.0k|          *utf8_output++ = char(word);
  572|  3.24M|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (572:20): [True: 56.5k, False: 3.18M]
  ------------------
  573|  56.5k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  574|  56.5k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  575|  3.18M|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (575:20): [True: 1.01M, False: 2.17M]
  ------------------
  576|  1.01M|          *utf8_output++ = char((word >> 12) | 0b11100000);
  577|  1.01M|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  578|  1.01M|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  579|  2.17M|        } else {
  580|       |          // must be a surrogate pair
  581|  2.17M|          uint16_t diff = uint16_t(word - 0xD800);
  582|  2.17M|          uint16_t next_word =
  583|  2.17M|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  584|  2.17M|          k++;
  585|  2.17M|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  586|  2.17M|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (586:15): [True: 2.11M, False: 64.0k]
  ------------------
  587|  2.11M|            return std::make_pair(
  588|  2.11M|                result(error_code::SURROGATE, buf - start + k - 1),
  589|  2.11M|                utf8_output);
  590|  2.11M|          }
  591|  64.0k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  592|  64.0k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  593|  64.0k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  594|  64.0k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  595|  64.0k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  596|  64.0k|        }
  597|  3.32M|      }
  598|  8.44k|      buf += k;
  599|  8.44k|    }
  600|  2.81M|  } // while
  601|  4.37k|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  602|  2.11M|}

_ZNK7simdutf7haswell14implementation13validate_utf8EPKcm:
  266|  2.48k|implementation::validate_utf8(const char *buf, size_t len) const noexcept {
  267|  2.48k|  return haswell::utf8_validation::generic_validate_utf8(buf, len);
  268|  2.48k|}
_ZNK7simdutf7haswell14implementation24utf8_length_from_utf16leEPKDsm:
 1114|    452|    const char16_t *input, size_t length) const noexcept {
 1115|    452|  return utf16::utf8_length_from_utf16_bytemask<endianness::LITTLE>(input,
 1116|    452|                                                                    length);
 1117|    452|}
_ZNK7simdutf7haswell14implementation24utf8_length_from_utf16beEPKDsm:
 1120|    402|    const char16_t *input, size_t length) const noexcept {
 1121|    402|  return utf16::utf8_length_from_utf16_bytemask<endianness::BIG>(input, length);
 1122|    402|}
_ZNK7simdutf7haswell14implementation41utf8_length_from_utf16le_with_replacementEPKDsm:
 1144|  1.27k|    const char16_t *input, size_t length) const noexcept {
 1145|  1.27k|  return utf16::utf8_length_from_utf16_with_replacement<endianness::LITTLE>(
 1146|  1.27k|      input, length);
 1147|  1.27k|}
_ZNK7simdutf7haswell14implementation41utf8_length_from_utf16be_with_replacementEPKDsm:
 1151|  1.20k|    const char16_t *input, size_t length) const noexcept {
 1152|  1.20k|  return utf16::utf8_length_from_utf16_with_replacement<endianness::BIG>(
 1153|  1.20k|      input, length);
 1154|  1.20k|}
_ZNK7simdutf7haswell14implementation40convert_utf16le_to_utf8_with_replacementEPKDsmPc:
 1158|  1.27k|    const char16_t *input, size_t length, char *utf8_buffer) const noexcept {
 1159|  1.27k|  return utf16_to_utf8::convert_with_replacement_via(
 1160|  1.27k|      [](const char16_t *b, size_t l, char *o) {
 1161|  1.27k|        return convert_utf16_to_utf8_with_details<endianness::LITTLE>(b, l, o);
 1162|  1.27k|      },
 1163|  1.27k|      input, length, utf8_buffer);
 1164|  1.27k|}
_ZNK7simdutf7haswell14implementation40convert_utf16be_to_utf8_with_replacementEPKDsmPc:
 1168|  1.20k|    const char16_t *input, size_t length, char *utf8_buffer) const noexcept {
 1169|  1.20k|  return utf16_to_utf8::convert_with_replacement_via(
 1170|  1.20k|      [](const char16_t *b, size_t l, char *o) {
 1171|  1.20k|        return convert_utf16_to_utf8_with_details<endianness::BIG>(b, l, o);
 1172|  1.20k|      },
 1173|  1.20k|      input, length, utf8_buffer);
 1174|  1.20k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_18is_asciiERKNS1_4simd8simd8x64IhEE:
   13|  1.96M|simdutf_really_inline bool is_ascii(const simd8x64<uint8_t> &input) {
   14|  1.96M|  return input.reduce_or().is_ascii();
   15|  1.96M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_124must_be_2_3_continuationENS1_4simd5simd8IhEES4_:
   22|  3.21M|                         const simd8<uint8_t> prev3) {
   23|  3.21M|  simd8<uint8_t> is_third_byte =
   24|  3.21M|      prev2.saturating_sub(0xe0u - 0x80); // Only 111_____ will be > 0x80
   25|  3.21M|  simd8<uint8_t> is_fourth_byte =
   26|  3.21M|      prev3.saturating_sub(0xf0u - 0x80); // Only 1111____ will be > 0x80
   27|  3.21M|  return simd8<bool>(is_third_byte | is_fourth_byte);
   28|  3.21M|}
_ZN7simdutf7haswell34convert_utf16_to_utf8_with_detailsILNS_10endiannessE0EEENS_11full_resultEPKDsmPc:
  722|  2.46M|    const char16_t *buf, size_t len, char *utf8_output) {
  723|  2.46M|  std::pair<result, char *> ret =
  724|  2.46M|      haswell::avx2_convert_utf16_to_utf8_with_errors<big_endian>(buf, len,
  725|  2.46M|                                                                  utf8_output);
  726|  2.46M|  if (ret.first.error) {
  ------------------
  |  Branch (726:7): [True: 2.46M, False: 4.19k]
  ------------------
  727|  2.46M|    return full_result(ret.first.error, ret.first.count,
  728|  2.46M|                       size_t(ret.second - utf8_output));
  729|  2.46M|  }
  730|  4.19k|  if (ret.first.count != len) {
  ------------------
  |  Branch (730:7): [True: 4.19k, False: 0]
  ------------------
  731|  4.19k|    full_result sres =
  732|  4.19k|        scalar::utf16_to_utf8::convert_with_errors<big_endian, false>(
  733|  4.19k|            buf + ret.first.count, len - ret.first.count, ret.second, 0);
  734|  4.19k|    return full_result(sres.error, ret.first.count + sres.input_count,
  735|  4.19k|                       size_t(ret.second - utf8_output) + sres.output_count);
  736|  4.19k|  }
  737|      0|  return full_result(error_code::SUCCESS, len,
  738|      0|                     size_t(ret.second - utf8_output));
  739|  4.19k|}
_ZN7simdutf7haswell34convert_utf16_to_utf8_with_detailsILNS_10endiannessE1EEENS_11full_resultEPKDsmPc:
  722|  2.11M|    const char16_t *buf, size_t len, char *utf8_output) {
  723|  2.11M|  std::pair<result, char *> ret =
  724|  2.11M|      haswell::avx2_convert_utf16_to_utf8_with_errors<big_endian>(buf, len,
  725|  2.11M|                                                                  utf8_output);
  726|  2.11M|  if (ret.first.error) {
  ------------------
  |  Branch (726:7): [True: 2.11M, False: 4.37k]
  ------------------
  727|  2.11M|    return full_result(ret.first.error, ret.first.count,
  728|  2.11M|                       size_t(ret.second - utf8_output));
  729|  2.11M|  }
  730|  4.37k|  if (ret.first.count != len) {
  ------------------
  |  Branch (730:7): [True: 4.37k, False: 0]
  ------------------
  731|  4.37k|    full_result sres =
  732|  4.37k|        scalar::utf16_to_utf8::convert_with_errors<big_endian, false>(
  733|  4.37k|            buf + ret.first.count, len - ret.first.count, ret.second, 0);
  734|  4.37k|    return full_result(sres.error, ret.first.count + sres.input_count,
  735|  4.37k|                       size_t(ret.second - utf8_output) + sres.output_count);
  736|  4.37k|  }
  737|      0|  return full_result(error_code::SUCCESS, len,
  738|      0|                     size_t(ret.second - utf8_output));
  739|  4.37k|}
simdutf.cpp:_ZZNK7simdutf7haswell14implementation40convert_utf16le_to_utf8_with_replacementEPKDsmPcENK3$_0clES3_mS4_:
 1160|  2.46M|      [](const char16_t *b, size_t l, char *o) {
 1161|  2.46M|        return convert_utf16_to_utf8_with_details<endianness::LITTLE>(b, l, o);
 1162|  2.46M|      },
simdutf.cpp:_ZZNK7simdutf7haswell14implementation40convert_utf16be_to_utf8_with_replacementEPKDsmPcENK3$_0clES3_mS4_:
 1170|  2.11M|      [](const char16_t *b, size_t l, char *o) {
 1171|  2.11M|        return convert_utf16_to_utf8_with_details<endianness::BIG>(b, l, o);
 1172|  2.11M|      },

_ZNK7simdutf14implementation27supported_by_runtime_systemEv:
   64|      8|bool implementation::supported_by_runtime_system() const {
   65|      8|  uint32_t required_instruction_sets = this->required_instruction_sets();
   66|      8|  uint32_t supported_instruction_sets =
   67|      8|      internal::detect_supported_architectures();
   68|      8|  return ((supported_instruction_sets & required_instruction_sets) ==
   69|      8|          required_instruction_sets);
   70|      8|}
_ZNK7simdutf8internal29available_implementation_list5beginEv:
 1513|      1|available_implementation_list::begin() const noexcept {
 1514|      1|  return internal::get_available_implementation_pointers().begin();
 1515|      1|}
_ZNK7simdutf8internal29available_implementation_list3endEv:
 1517|      1|available_implementation_list::end() const noexcept {
 1518|      1|  return internal::get_available_implementation_pointers().end();
 1519|      1|}
_ZN7simdutf29get_available_implementationsEv:
 1568|      1|get_available_implementations() {
 1569|      1|#if !SIMDUTF_USE_STATIC_INITIALIZATION
 1570|      1|  static const internal::available_implementation_list
 1571|      1|      available_implementations_instance{};
 1572|      1|#endif
 1573|      1|  return available_implementations_instance;
 1574|      1|}
simdutf.cpp:_ZN7simdutf8internalL37get_available_implementation_pointersEv:
  938|      2|get_available_implementation_pointers() {
  939|      2|#if !SIMDUTF_USE_STATIC_INITIALIZATION
  940|      2|  static const std::initializer_list<const implementation *>
  941|      2|      available_implementation_pointers{
  942|      2|  #if SIMDUTF_IMPLEMENTATION_ICELAKE
  943|      2|          get_icelake_singleton(),
  944|      2|  #endif
  945|      2|  #if SIMDUTF_IMPLEMENTATION_HASWELL
  946|      2|          get_haswell_singleton(),
  947|      2|  #endif
  948|      2|  #if SIMDUTF_IMPLEMENTATION_WESTMERE
  949|      2|          get_westmere_singleton(),
  950|      2|  #endif
  951|       |  #if SIMDUTF_IMPLEMENTATION_ARM64
  952|       |          get_arm64_singleton(),
  953|       |  #endif
  954|       |  #if SIMDUTF_IMPLEMENTATION_PPC64
  955|       |          get_ppc64_singleton(),
  956|       |  #endif
  957|       |  #if SIMDUTF_IMPLEMENTATION_RVV
  958|       |          get_rvv_singleton(),
  959|       |  #endif
  960|       |  #if SIMDUTF_IMPLEMENTATION_LASX
  961|       |          get_lasx_singleton(),
  962|       |  #endif
  963|       |  #if SIMDUTF_IMPLEMENTATION_LSX
  964|       |          get_lsx_singleton(),
  965|       |  #endif
  966|      2|  #if SIMDUTF_IMPLEMENTATION_FALLBACK
  967|      2|          get_fallback_singleton(),
  968|      2|  #endif
  969|      2|      };
  970|      2|#endif
  971|      2|  return available_implementation_pointers;
  972|      2|}
simdutf.cpp:_ZN7simdutf8internalL21get_icelake_singletonEv:
  157|      1|static const icelake::implementation *get_icelake_singleton() {
  158|      1|  #if !SIMDUTF_USE_STATIC_INITIALIZATION
  159|      1|  static const icelake::implementation icelake_singleton{};
  160|      1|  #endif
  161|      1|  return &icelake_singleton;
  162|      1|}
simdutf.cpp:_ZN7simdutf8internalL21get_haswell_singletonEv:
  168|      1|static const haswell::implementation *get_haswell_singleton() {
  169|      1|  #if !SIMDUTF_USE_STATIC_INITIALIZATION
  170|      1|  static const haswell::implementation haswell_singleton{};
  171|      1|  #endif
  172|      1|  return &haswell_singleton;
  173|      1|}
simdutf.cpp:_ZN7simdutf8internalL22get_westmere_singletonEv:
  179|      1|static const westmere::implementation *get_westmere_singleton() {
  180|      1|  #if !SIMDUTF_USE_STATIC_INITIALIZATION
  181|      1|  static const westmere::implementation westmere_singleton{};
  182|      1|  #endif
  183|      1|  return &westmere_singleton;
  184|      1|}
simdutf.cpp:_ZN7simdutf8internalL22get_fallback_singletonEv:
  245|      1|static const fallback::implementation *get_fallback_singleton() {
  246|      1|  #if !SIMDUTF_USE_STATIC_INITIALIZATION
  247|      1|  static const fallback::implementation fallback_singleton{};
  248|      1|  #endif
  249|      1|  return &fallback_singleton;
  250|      1|}

_ZN7simdutf8fallback14implementationC2Ev:
   16|      1|      : simdutf::implementation("fallback", "Generic fallback implementation",
   17|      1|                                0) {}

_ZN7simdutf7haswell14implementationC2Ev:
   16|      1|      : simdutf::implementation("haswell", "Intel/AMD AVX2",
   17|      1|                                internal::instruction_set::AVX2 |
   18|      1|                                    internal::instruction_set::BMI1 |
   19|      1|                                    internal::instruction_set::BMI2) {}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEC2Ev:
  191|  7.44k|  simdutf_really_inline simd8() : base8_numeric<uint8_t>() {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhEC2Ev:
  124|  7.44k|  simdutf_really_inline base8_numeric() : base8<T>() {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEEC2Ev:
   71|  7.44k|  simdutf_really_inline base8() : base<simd8<T>>() {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEC2Ev:
   14|  7.44k|  simdutf_really_inline base() : value{__m256i()} {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IhE9reduce_orEv:
  294|  1.96M|  simdutf_really_inline simd8<T> reduce_or() const {
  295|  1.96M|    return this->chunks[0] | this->chunks[1];
  296|  1.96M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEorES5_:
   50|  8.76M|  simdutf_really_inline Child operator|(const Child other) const {
   51|  8.76M|    return _mm256_or_si256(*this, other);
   52|  8.76M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEC2EDv4_x:
  193|   102M|      : base8_numeric<uint8_t>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhEC2EDv4_x:
  126|   102M|      : base8<T>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEEC2EDv4_x:
   73|   102M|  simdutf_really_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEC2EDv4_x:
   17|   102M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE8is_asciiEv:
  234|  1.96M|  simdutf_really_inline bool is_ascii() const {
  235|  1.96M|    return _mm256_movemask_epi8(*this) == 0;
  236|  1.96M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEoRES5_:
   59|  3.57M|  simdutf_really_inline Child &operator|=(const Child other) {
   60|  3.57M|    auto this_cast = static_cast<Child *>(this);
   61|  3.57M|    *this_cast = *this_cast | other;
   62|  3.57M|    return *this_cast;
   63|  3.57M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi1EEENS4_IhEES8_:
   83|  3.21M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   84|       |    return _mm256_alignr_epi8(
   85|  3.21M|        *this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N);
   86|  3.21M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE3shrILi4EEES4_v:
  245|  6.43M|  template <int N> simdutf_really_inline simd8<uint8_t> shr() const {
  246|  6.43M|    return simd8<uint8_t>(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N);
  247|  6.43M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE9lookup_16IhEENS2_5simd8IT_EES7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_:
  157|  9.65M|            L replace15) const {
  158|  9.65M|    return lookup_16(simd8<L>::repeat_16(
  159|  9.65M|        replace0, replace1, replace2, replace3, replace4, replace5, replace6,
  160|  9.65M|        replace7, replace8, replace9, replace10, replace11, replace12,
  161|  9.65M|        replace13, replace14, replace15));
  162|  9.65M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE9lookup_16IhEENS2_5simd8IT_EES8_:
  148|  9.65M|  simdutf_really_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
  149|  9.65M|    return _mm256_shuffle_epi8(lookup_table, *this);
  150|  9.65M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE9repeat_16Ehhhhhhhhhhhhhhhh:
  118|  9.65M|                                                  T v14, T v15) {
  119|  9.65M|    return simd8<T>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
  120|  9.65M|                    v14, v15, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
  121|  9.65M|                    v12, v13, v14, v15);
  122|  9.65M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEC2Ehhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh:
  207|  9.65M|      : simd8(_mm256_setr_epi8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
  208|  9.65M|                               v12, v13, v14, v15, v16, v17, v18, v19, v20, v21,
  209|  9.65M|                               v22, v23, v24, v25, v26, v27, v28, v29, v30,
  210|  9.65M|                               v31)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEC2Eh:
  195|  19.3M|  simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi2EEENS4_IhEES8_:
   83|  3.21M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   84|       |    return _mm256_alignr_epi8(
   85|  3.21M|        *this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N);
   86|  3.21M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi3EEENS4_IhEES8_:
   83|  3.21M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   84|       |    return _mm256_alignr_epi8(
   85|  3.21M|        *this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N);
   86|  3.21M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE14saturating_subES4_:
  214|  8.04M|  saturating_sub(const simd8<uint8_t> other) const {
  215|  8.04M|    return _mm256_subs_epu8(*this, other);
  216|  8.04M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IbEC2EDv4_x:
   95|  3.21M|  simdutf_really_inline simd8(const __m256i _value) : base8<bool>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5base8IbNS2_5simd8IbEEEC2EDv4_x:
   73|  3.21M|  simdutf_really_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IbEEEC2EDv4_x:
   17|  3.21M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IbEEEcvRKDv4_xEv:
   19|  3.21M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEeoES5_:
   56|  3.21M|  simdutf_really_inline Child operator^(const Child other) const {
   57|  3.21M|    return _mm256_xor_si256(*this, other);
   58|  3.21M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEC2EPKh:
  197|  1.60M|  simdutf_really_inline simd8(const uint8_t values[32]) : simd8(load(values)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE4loadEPKh:
  111|  5.54M|  static simdutf_really_inline simd8<T> load(const T values[32]) {
  112|  5.54M|    return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(values));
  113|  5.54M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE7gt_bitsES4_:
  225|  1.60M|  gt_bits(const simd8<uint8_t> other) const {
  226|  1.60M|    return this->saturating_sub(other);
  227|  1.60M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEC2EDv4_x:
   17|  80.3M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEcvRKDv4_xEv:
   19|  80.2M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IbE10to_bitmaskEv:
   99|  1.63k|  simdutf_really_inline uint32_t to_bitmask() const {
  100|  1.63k|    return uint32_t(_mm256_movemask_epi8(value));
  101|  1.63k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE21any_bits_set_anywhereEv:
  241|  2.48k|  simdutf_really_inline bool any_bits_set_anywhere() const {
  242|  2.48k|    return !bits_not_set_anywhere();
  243|  2.48k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE21bits_not_set_anywhereEv:
  237|  2.48k|  simdutf_really_inline bool bits_not_set_anywhere() const {
  238|  2.48k|    return _mm256_testz_si256(*this, *this);
  239|  2.48k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IhE8is_asciiEv:
  298|    913|  simdutf_really_inline bool is_ascii() const {
  299|    913|    return this->reduce_or().is_ascii();
  300|    913|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IhE13gteq_unsignedEh:
  341|    815|  simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const {
  342|    815|    const simd8<uint8_t> mask = simd8<uint8_t>::splat(m);
  343|    815|    return simd8x64<bool>((simd8<uint8_t>(__m256i(this->chunks[0])) >= mask),
  344|    815|                          (simd8<uint8_t>(__m256i(this->chunks[1])) >= mask))
  345|    815|        .to_bitmask();
  346|    815|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEgeES4_:
  229|  1.63k|  operator>=(const simd8<uint8_t> other) const {
  230|  1.63k|    return other.min_val(*this) == other;
  231|  1.63k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE7min_valES4_:
  220|  1.63k|  min_val(const simd8<uint8_t> other) const {
  221|  1.63k|    return _mm256_min_epu8(other, *this);
  222|  1.63k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IbEC2ENS2_5simd8IbEES6_:
  272|    815|      : chunks{chunk0, chunk1} {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IbE10to_bitmaskEv:
  282|    815|  simdutf_really_inline uint64_t to_bitmask() const {
  283|    815|    uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask());
  284|    815|    uint64_t r_hi = this->chunks[1].to_bitmask();
  285|    815|    return r_lo | (r_hi << 32);
  286|    815|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEC2EDv4_x:
   17|  6.67M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEcvRKDv4_xEv:
   19|  10.3M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE5splatEh:
  105|  19.3M|  static simdutf_really_inline simd8<T> splat(T _value) {
  106|  19.3M|    return _mm256_set1_epi8(_value);
  107|  19.3M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEcvRKDv4_xEv:
   19|   138M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simdeqENS2_5simd8IhEES4_:
   76|  1.63k|                                               const simd8<T> rhs) {
   77|  1.63k|    return _mm256_cmpeq_epi8(lhs, rhs);
   78|  1.63k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IhEC2EPKh:
  274|  1.96M|      : chunks{simd8<T>::load(ptr),
  275|  1.96M|               simd8<T>::load(ptr + sizeof(simd8<T>) / sizeof(T))} {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEanES5_:
   53|  19.3M|  simdutf_really_inline Child operator&(const Child other) const {
   54|  19.3M|    return _mm256_and_si256(*this, other);
   55|  19.3M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEanES5_:
   53|  16.6M|  simdutf_really_inline Child operator&(const Child other) const {
   54|  16.6M|    return _mm256_and_si256(*this, other);
   55|  16.6M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEeoES5_:
   56|   484k|  simdutf_really_inline Child operator^(const Child other) const {
   57|   484k|    return _mm256_xor_si256(*this, other);
   58|   484k|  }

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd16ItEC2EDv4_x:
   97|  80.3M|      : base16_numeric<uint16_t>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItEC2EDv4_x:
   73|  80.3M|      : base16<T>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6base16ItNS2_6simd16IbEEEC2EDv4_x:
   20|  80.3M|      : base<simd16<T>>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItE4loadEPKt:
   66|  5.70M|  static simdutf_really_inline simd16<T> load(const T values[8]) {
   67|  5.70M|    return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(values));
   68|  5.70M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd16ItE10swap_bytesEv:
  139|  2.40M|  simdutf_really_inline simd16<uint16_t> swap_bytes() const {
  140|  2.40M|    const __m256i swap = _mm256_setr_epi8(
  141|  2.40M|        1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
  142|  2.40M|        21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
  143|  2.40M|    return _mm256_shuffle_epi8(*this, swap);
  144|  2.40M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItE5splatEt:
   58|  22.8M|  static simdutf_really_inline simd16<T> splat(T _value) {
   59|  22.8M|    return _mm256_set1_epi16(_value);
   60|  22.8M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simdeqENS2_6simd16ItEES4_:
   26|  6.18M|                                               const simd16<T> rhs) {
   27|  6.18M|    return _mm256_cmpeq_epi16(lhs, rhs);
   28|  6.18M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd16IbEC2EDv4_x:
   45|  6.67M|  simdutf_really_inline simd16(const __m256i _value) : base16<bool>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6base16IbNS2_6simd16IbEEEC2EDv4_x:
   20|  6.67M|      : base<simd16<T>>(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd16IbE10to_bitmaskEv:
   50|  3.68M|  simdutf_really_inline bitmask_type to_bitmask() const {
   51|  3.68M|    return _mm256_movemask_epi8(*this);
   52|  3.68M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd16ItEC2Et:
  100|  22.8M|  simdutf_really_inline simd16(uint16_t _value) : simd16(splat(_value)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItE4zeroEv:
   62|  4.32k|  static simdutf_really_inline simd16<T> zero() {
   63|  4.32k|    return _mm256_setzero_si256();
   64|  4.32k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd3minENS2_6simd16ItEES4_:
  259|  10.9M|simd16<uint16_t> min(const simd16<uint16_t> a, simd16<uint16_t> b) {
  260|  10.9M|  return _mm256_min_epu16(a.value, b.value);
  261|  10.9M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItEpLENS2_6simd16ItEE:
   87|  16.1M|  simdutf_really_inline simd16<T> &operator+=(const simd16<T> other) {
   88|  16.1M|    *this = *this + other;
   89|  16.1M|    return *static_cast<simd16<T> *>(this);
   90|  16.1M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItEplENS2_6simd16ItEE:
   84|  16.1M|  simdutf_really_inline simd16<T> operator+(const simd16<T> other) const {
   85|  16.1M|    return _mm256_add_epi16(*this, other);
   86|  16.1M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd16ItE3sumEv:
  170|  4.32k|  simdutf_really_inline uint64_t sum() const {
  171|  4.32k|    const auto lo_u16 = _mm256_and_si256(value, _mm256_set1_epi32(0x0000ffff));
  172|  4.32k|    const auto hi_u16 = _mm256_srli_epi32(value, 16);
  173|  4.32k|    const auto sum_u32 = _mm256_add_epi32(lo_u16, hi_u16);
  174|       |
  175|  4.32k|    const auto lo_u32 =
  176|  4.32k|        _mm256_and_si256(sum_u32, _mm256_set1_epi64x(0xffffffff));
  177|  4.32k|    const auto hi_u32 = _mm256_srli_epi64(sum_u32, 32);
  178|  4.32k|    const auto sum_u64 = _mm256_add_epi64(lo_u32, hi_u32);
  179|       |
  180|  4.32k|    return uint64_t(_mm256_extract_epi64(sum_u64, 0)) +
  181|  4.32k|           uint64_t(_mm256_extract_epi64(sum_u64, 1)) +
  182|  4.32k|           uint64_t(_mm256_extract_epi64(sum_u64, 2)) +
  183|       |           uint64_t(_mm256_extract_epi64(sum_u64, 3));
  184|  4.32k|  }

_ZN7simdutf7icelake14implementationC2Ev:
   16|      1|      : simdutf::implementation(
   17|      1|            "icelake",
   18|      1|            "Intel AVX512 (AVX-512BW, AVX-512CD, AVX-512VL, AVX-512VBMI2 "
   19|      1|            "extensions)",
   20|      1|            internal::instruction_set::AVX2 | internal::instruction_set::BMI1 |
   21|      1|                internal::instruction_set::BMI2 |
   22|      1|                internal::instruction_set::AVX512BW |
   23|      1|                internal::instruction_set::AVX512CD |
   24|      1|                internal::instruction_set::AVX512VL |
   25|      1|                internal::instruction_set::AVX512VBMI2 |
   26|      1|                internal::instruction_set::AVX512VPOPCNTDQ) {}

_ZN7simdutf8westmere14implementationC2Ev:
   18|      1|      : simdutf::implementation("westmere", "Intel/AMD SSE4.2",
   19|      1|                                internal::instruction_set::SSE42) {}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2Ev:
  183|  7.44k|  simdutf_really_inline simd8() : base8_numeric<uint8_t>() {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhEC2Ev:
  118|  7.44k|  simdutf_really_inline base8_numeric() : base8<T>() {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEEC2Ev:
   68|  7.44k|  simdutf_really_inline base8() : base<simd8<T>>() {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEC2Ev:
   13|  7.44k|  simdutf_really_inline base() : value{__m128i()} {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IhE9reduce_orEv:
  290|  1.96M|  simdutf_really_inline simd8<T> reduce_or() const {
  291|  1.96M|    return (this->chunks[0] | this->chunks[1]) |
  292|  1.96M|           (this->chunks[2] | this->chunks[3]);
  293|  1.96M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEorES5_:
   42|  19.1M|  simdutf_really_inline Child operator|(const Child other) const {
   43|  19.1M|    return _mm_or_si128(*this, other);
   44|  19.1M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2EDv2_x:
  185|   204M|      : base8_numeric<uint8_t>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhEC2EDv2_x:
  120|   204M|      : base8<T>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEEC2EDv2_x:
   69|   204M|  simdutf_really_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEC2EDv2_x:
   16|   204M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE8is_asciiEv:
  228|  1.96M|  simdutf_really_inline bool is_ascii() const {
  229|  1.96M|    return _mm_movemask_epi8(*this) == 0;
  230|  1.96M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEoRES5_:
   51|  6.79M|  simdutf_really_inline Child &operator|=(const Child other) {
   52|  6.79M|    auto this_cast = static_cast<Child *>(this);
   53|  6.79M|    *this_cast = *this_cast | other;
   54|  6.79M|    return *this_cast;
   55|  6.79M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi1EEENS4_IhEES8_:
   79|  6.43M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   80|       |    return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
   81|  6.43M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE3shrILi4EEES4_v:
  238|  12.8M|  template <int N> simdutf_really_inline simd8<uint8_t> shr() const {
  239|  12.8M|    return simd8<uint8_t>(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N);
  240|  12.8M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE9lookup_16IhEENS2_5simd8IT_EES7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_:
  151|  19.3M|            L replace15) const {
  152|  19.3M|    return lookup_16(simd8<L>::repeat_16(
  153|  19.3M|        replace0, replace1, replace2, replace3, replace4, replace5, replace6,
  154|  19.3M|        replace7, replace8, replace9, replace10, replace11, replace12,
  155|  19.3M|        replace13, replace14, replace15));
  156|  19.3M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE9lookup_16IhEENS2_5simd8IT_EES8_:
  142|  19.3M|  simdutf_really_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
  143|  19.3M|    return _mm_shuffle_epi8(lookup_table, *this);
  144|  19.3M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE9repeat_16Ehhhhhhhhhhhhhhhh:
  113|  19.3M|                                                  T v14, T v15) {
  114|  19.3M|    return simd8<T>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
  115|  19.3M|                    v14, v15);
  116|  19.3M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2Ehhhhhhhhhhhhhhhh:
  196|  19.3M|      : simd8(_mm_setr_epi8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
  197|  19.3M|                            v12, v13, v14, v15)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2Eh:
  188|  38.6M|  simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi2EEENS4_IhEES8_:
   79|  6.43M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   80|       |    return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
   81|  6.43M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi3EEENS4_IhEES8_:
   79|  6.43M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   80|       |    return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
   81|  6.43M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE14saturating_subES4_:
  201|  14.4M|  saturating_sub(const simd8<uint8_t> other) const {
  202|  14.4M|    return _mm_subs_epu8(*this, other);
  203|  14.4M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IbEC2EDv2_x:
   91|  6.44M|  simdutf_really_inline simd8(const __m128i _value) : base8<bool>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5base8IbNS2_5simd8IbEEEC2EDv2_x:
   69|  6.44M|  simdutf_really_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IbEEEC2EDv2_x:
   16|  6.44M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IbEEEcvRKDv2_xEv:
   18|  6.44M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEeoES5_:
   48|  6.43M|  simdutf_really_inline Child operator^(const Child other) const {
   49|  6.43M|    return _mm_xor_si128(*this, other);
   50|  6.43M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2EPKh:
  190|  1.61M|  simdutf_really_inline simd8(const uint8_t *values) : simd8(load(values)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE4loadEPKh:
  106|  9.47M|  static simdutf_really_inline simd8<T> load(const T values[16]) {
  107|  9.47M|    return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
  108|  9.47M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE7gt_bitsES4_:
  212|  1.60M|  gt_bits(const simd8<uint8_t> other) const {
  213|  1.60M|    return this->saturating_sub(other);
  214|  1.60M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEC2EDv2_x:
   16|   159M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEcvRKDv2_xEv:
   18|   159M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IbE10to_bitmaskEv:
   95|  3.55k|  simdutf_really_inline int to_bitmask() const {
   96|  3.55k|    return _mm_movemask_epi8(*this);
   97|  3.55k|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE21any_bits_set_anywhereEv:
  235|  2.48k|  simdutf_really_inline bool any_bits_set_anywhere() const {
  236|  2.48k|    return !bits_not_set_anywhere();
  237|  2.48k|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE21bits_not_set_anywhereEv:
  232|  2.48k|  simdutf_really_inline bool bits_not_set_anywhere() const {
  233|  2.48k|    return _mm_testz_si128(*this, *this);
  234|  2.48k|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IhE8is_asciiEv:
  295|    988|  simdutf_really_inline bool is_ascii() const {
  296|    988|    return this->reduce_or().is_ascii();
  297|    988|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IhE13gteq_unsignedEh:
  352|    889|  simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const {
  353|    889|    const simd8<uint8_t> mask = simd8<uint8_t>::splat(m);
  354|    889|    return simd8x64<bool>(simd8<uint8_t>(__m128i(this->chunks[0])) >= mask,
  355|    889|                          simd8<uint8_t>(__m128i(this->chunks[1])) >= mask,
  356|    889|                          simd8<uint8_t>(__m128i(this->chunks[2])) >= mask,
  357|    889|                          simd8<uint8_t>(__m128i(this->chunks[3])) >= mask)
  358|    889|        .to_bitmask();
  359|    889|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEgeES4_:
  217|  3.55k|  operator>=(const simd8<uint8_t> other) const {
  218|  3.55k|    return other.min_val(*this) == other;
  219|  3.55k|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE7min_valES4_:
  207|  3.55k|  min_val(const simd8<uint8_t> other) const {
  208|  3.55k|    return _mm_min_epu8(*this, other);
  209|  3.55k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IbEC2ENS2_5simd8IbEES6_S6_S6_:
  268|    889|      : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IbE10to_bitmaskEv:
  318|    889|  simdutf_really_inline uint64_t to_bitmask() const {
  319|    889|    uint64_t r0 = uint32_t(this->chunks[0].to_bitmask());
  320|    889|    uint64_t r1 = this->chunks[1].to_bitmask();
  321|    889|    uint64_t r2 = this->chunks[2].to_bitmask();
  322|    889|    uint64_t r3 = this->chunks[3].to_bitmask();
  323|    889|    return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
  324|    889|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEC2EDv2_x:
   16|  12.9M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEcvRKDv2_xEv:
   18|  20.2M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE5splatEh:
  102|  38.6M|  static simdutf_really_inline simd8<T> splat(T _value) {
  103|  38.6M|    return _mm_set1_epi8(_value);
  104|  38.6M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEcvRKDv2_xEv:
   18|   255M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdeqENS2_5simd8IhEES4_:
   72|  3.55k|                                               const simd8<T> rhs) {
   73|  3.55k|    return _mm_cmpeq_epi8(lhs, rhs);
   74|  3.55k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IhEC2EPKh:
  270|  1.96M|      : chunks{simd8<T>::load(ptr),
  271|  1.96M|               simd8<T>::load(ptr + sizeof(simd8<T>) / sizeof(T)),
  272|  1.96M|               simd8<T>::load(ptr + 2 * sizeof(simd8<T>) / sizeof(T)),
  273|  1.96M|               simd8<T>::load(ptr + 3 * sizeof(simd8<T>) / sizeof(T))} {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEanES5_:
   45|  38.6M|  simdutf_really_inline Child operator&(const Child other) const {
   46|  38.6M|    return _mm_and_si128(*this, other);
   47|  38.6M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEanES5_:
   45|  32.9M|  simdutf_really_inline Child operator&(const Child other) const {
   46|  32.9M|    return _mm_and_si128(*this, other);
   47|  32.9M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEeoES5_:
   48|   825k|  simdutf_really_inline Child operator^(const Child other) const {
   49|   825k|    return _mm_xor_si128(*this, other);
   50|   825k|  }

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd16ItEC2EDv2_x:
   79|   159M|      : base16_numeric<uint16_t>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItEC2EDv2_x:
   54|   159M|      : base16<T>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6base16ItNS2_6simd16IbEEEC2EDv2_x:
    8|   159M|      : base<simd16<T>>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItE4loadEPKt:
   47|  11.2M|  static simdutf_really_inline simd16<T> load(const T values[8]) {
   48|  11.2M|    return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
   49|  11.2M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd16ItE10swap_bytesEv:
  121|  4.72M|  simdutf_really_inline simd16<uint16_t> swap_bytes() const {
  122|  4.72M|    const __m128i swap =
  123|  4.72M|        _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  124|  4.72M|    return _mm_shuffle_epi8(*this, swap);
  125|  4.72M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItE5splatEt:
   41|  45.0M|  static simdutf_really_inline simd16<T> splat(T _value) {
   42|  45.0M|    return _mm_set1_epi16(_value);
   43|  45.0M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdeqENS2_6simd16ItEES4_:
   11|  12.0M|                                               const simd16<T> rhs) {
   12|  12.0M|    return _mm_cmpeq_epi16(lhs, rhs);
   13|  12.0M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd16IbEC2EDv2_x:
   28|  12.9M|  simdutf_really_inline simd16(const __m128i _value) : base16<bool>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6base16IbNS2_6simd16IbEEEC2EDv2_x:
    8|  12.9M|      : base<simd16<T>>(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd16IbE10to_bitmaskEv:
   33|  7.36M|  simdutf_really_inline int to_bitmask() const {
   34|  7.36M|    return _mm_movemask_epi8(*this);
   35|  7.36M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd16ItEC2Et:
   82|  45.0M|  simdutf_really_inline simd16(uint16_t _value) : simd16(splat(_value)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItE4zeroEv:
   45|  5.36k|  static simdutf_really_inline simd16<T> zero() { return _mm_setzero_si128(); }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd3minENS2_6simd16ItEES4_:
  240|  21.6M|simd16<uint16_t> min(const simd16<uint16_t> a, simd16<uint16_t> b) {
  241|  21.6M|  return _mm_min_epu16(a.value, b.value);
  242|  21.6M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItEpLENS2_6simd16ItEE:
   68|  32.1M|  simdutf_really_inline simd16<T> &operator+=(const simd16<T> other) {
   69|  32.1M|    *this = *this + other;
   70|  32.1M|    return *static_cast<simd16<T> *>(this);
   71|  32.1M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItEplENS2_6simd16ItEE:
   65|  32.1M|  simdutf_really_inline simd16<T> operator+(const simd16<T> other) const {
   66|  32.1M|    return _mm_add_epi16(*this, other);
   67|  32.1M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd16ItE3sumEv:
  134|  5.36k|  simdutf_really_inline uint64_t sum() const {
  135|  5.36k|    const auto lo_u16 = _mm_and_si128(value, _mm_set1_epi32(0x0000ffff));
  136|  5.36k|    const auto hi_u16 = _mm_srli_epi32(value, 16);
  137|  5.36k|    const auto sum_u32 = _mm_add_epi32(lo_u16, hi_u16);
  138|       |
  139|  5.36k|    const auto lo_u32 = _mm_and_si128(sum_u32, _mm_set1_epi64x(0xffffffff));
  140|  5.36k|    const auto hi_u32 = _mm_srli_epi64(sum_u32, 32);
  141|  5.36k|    const auto sum_u64 = _mm_add_epi64(lo_u32, hi_u32);
  142|       |
  143|  5.36k|    return uint64_t(_mm_extract_epi64(sum_u64, 0)) +
  144|       |           uint64_t(_mm_extract_epi64(sum_u64, 1));
  145|  5.36k|  }

_ZNK7simdutf8westmere14implementation13validate_utf8EPKcm:
  299|  2.48k|implementation::validate_utf8(const char *buf, size_t len) const noexcept {
  300|  2.48k|  return westmere::utf8_validation::generic_validate_utf8(buf, len);
  301|  2.48k|}
_ZNK7simdutf8westmere14implementation24utf8_length_from_utf16leEPKDsm:
 1140|    452|    const char16_t *input, size_t length) const noexcept {
 1141|    452|  return utf16::utf8_length_from_utf16_bytemask<endianness::LITTLE>(input,
 1142|    452|                                                                    length);
 1143|    452|}
_ZNK7simdutf8westmere14implementation24utf8_length_from_utf16beEPKDsm:
 1146|    402|    const char16_t *input, size_t length) const noexcept {
 1147|    402|  return utf16::utf8_length_from_utf16_bytemask<endianness::BIG>(input, length);
 1148|    402|}
_ZNK7simdutf8westmere14implementation41utf8_length_from_utf16le_with_replacementEPKDsm:
 1232|  1.27k|    const char16_t *input, size_t length) const noexcept {
 1233|  1.27k|  return utf16::utf8_length_from_utf16_with_replacement<endianness::LITTLE>(
 1234|  1.27k|      input, length);
 1235|  1.27k|}
_ZNK7simdutf8westmere14implementation41utf8_length_from_utf16be_with_replacementEPKDsm:
 1239|  1.20k|    const char16_t *input, size_t length) const noexcept {
 1240|  1.20k|  return utf16::utf8_length_from_utf16_with_replacement<endianness::BIG>(
 1241|  1.20k|      input, length);
 1242|  1.20k|}
_ZNK7simdutf8westmere14implementation40convert_utf16le_to_utf8_with_replacementEPKDsmPc:
 1246|  1.27k|    const char16_t *input, size_t length, char *utf8_buffer) const noexcept {
 1247|  1.27k|  return utf16_to_utf8::convert_with_replacement_via(
 1248|  1.27k|      [](const char16_t *b, size_t l, char *o) {
 1249|  1.27k|        return convert_utf16_to_utf8_with_details<endianness::LITTLE>(b, l, o);
 1250|  1.27k|      },
 1251|  1.27k|      input, length, utf8_buffer);
 1252|  1.27k|}
_ZNK7simdutf8westmere14implementation40convert_utf16be_to_utf8_with_replacementEPKDsmPc:
 1256|  1.20k|    const char16_t *input, size_t length, char *utf8_buffer) const noexcept {
 1257|  1.20k|  return utf16_to_utf8::convert_with_replacement_via(
 1258|  1.20k|      [](const char16_t *b, size_t l, char *o) {
 1259|  1.20k|        return convert_utf16_to_utf8_with_details<endianness::BIG>(b, l, o);
 1260|  1.20k|      },
 1261|  1.20k|      input, length, utf8_buffer);
 1262|  1.20k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_18is_asciiERKNS1_4simd8simd8x64IhEE:
   13|  1.96M|simdutf_really_inline bool is_ascii(const simd8x64<uint8_t> &input) {
   14|  1.96M|  return input.reduce_or().is_ascii();
   15|  1.96M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_124must_be_2_3_continuationENS1_4simd5simd8IhEES4_:
   22|  6.43M|                         const simd8<uint8_t> prev3) {
   23|  6.43M|  simd8<uint8_t> is_third_byte =
   24|  6.43M|      prev2.saturating_sub(0xe0u - 0x80); // Only 111_____ will be >= 0x80
   25|  6.43M|  simd8<uint8_t> is_fourth_byte =
   26|  6.43M|      prev3.saturating_sub(0xf0u - 0x80); // Only 1111____ will be >= 0x80
   27|  6.43M|  return simd8<bool>(is_third_byte | is_fourth_byte);
   28|  6.43M|}
_ZN7simdutf8westmere34convert_utf16_to_utf8_with_detailsILNS_10endiannessE0EEENS_11full_resultEPKDsmPc:
  750|  2.46M|    const char16_t *buf, size_t len, char *utf8_output) {
  751|  2.46M|  std::pair<result, char *> ret =
  752|  2.46M|      westmere::sse_convert_utf16_to_utf8_with_errors<big_endian>(buf, len,
  753|  2.46M|                                                                  utf8_output);
  754|  2.46M|  if (ret.first.error) {
  ------------------
  |  Branch (754:7): [True: 2.46M, False: 4.21k]
  ------------------
  755|  2.46M|    return full_result(ret.first.error, ret.first.count,
  756|  2.46M|                       size_t(ret.second - utf8_output));
  757|  2.46M|  }
  758|  4.21k|  if (ret.first.count != len) {
  ------------------
  |  Branch (758:7): [True: 4.21k, False: 0]
  ------------------
  759|  4.21k|    full_result sres =
  760|  4.21k|        scalar::utf16_to_utf8::convert_with_errors<big_endian, false>(
  761|  4.21k|            buf + ret.first.count, len - ret.first.count, ret.second, 0);
  762|  4.21k|    return full_result(sres.error, ret.first.count + sres.input_count,
  763|  4.21k|                       size_t(ret.second - utf8_output) + sres.output_count);
  764|  4.21k|  }
  765|      0|  return full_result(error_code::SUCCESS, len,
  766|      0|                     size_t(ret.second - utf8_output));
  767|  4.21k|}
_ZN7simdutf8westmere34convert_utf16_to_utf8_with_detailsILNS_10endiannessE1EEENS_11full_resultEPKDsmPc:
  750|  2.11M|    const char16_t *buf, size_t len, char *utf8_output) {
  751|  2.11M|  std::pair<result, char *> ret =
  752|  2.11M|      westmere::sse_convert_utf16_to_utf8_with_errors<big_endian>(buf, len,
  753|  2.11M|                                                                  utf8_output);
  754|  2.11M|  if (ret.first.error) {
  ------------------
  |  Branch (754:7): [True: 2.11M, False: 4.40k]
  ------------------
  755|  2.11M|    return full_result(ret.first.error, ret.first.count,
  756|  2.11M|                       size_t(ret.second - utf8_output));
  757|  2.11M|  }
  758|  4.40k|  if (ret.first.count != len) {
  ------------------
  |  Branch (758:7): [True: 4.40k, False: 0]
  ------------------
  759|  4.40k|    full_result sres =
  760|  4.40k|        scalar::utf16_to_utf8::convert_with_errors<big_endian, false>(
  761|  4.40k|            buf + ret.first.count, len - ret.first.count, ret.second, 0);
  762|  4.40k|    return full_result(sres.error, ret.first.count + sres.input_count,
  763|  4.40k|                       size_t(ret.second - utf8_output) + sres.output_count);
  764|  4.40k|  }
  765|      0|  return full_result(error_code::SUCCESS, len,
  766|      0|                     size_t(ret.second - utf8_output));
  767|  4.40k|}
simdutf.cpp:_ZZNK7simdutf8westmere14implementation40convert_utf16le_to_utf8_with_replacementEPKDsmPcENK3$_0clES3_mS4_:
 1248|  2.46M|      [](const char16_t *b, size_t l, char *o) {
 1249|  2.46M|        return convert_utf16_to_utf8_with_details<endianness::LITTLE>(b, l, o);
 1250|  2.46M|      },
simdutf.cpp:_ZZNK7simdutf8westmere14implementation40convert_utf16be_to_utf8_with_replacementEPKDsmPcENK3$_0clES3_mS4_:
 1258|  2.11M|      [](const char16_t *b, size_t l, char *o) {
 1259|  2.11M|        return convert_utf16_to_utf8_with_details<endianness::BIG>(b, l, o);
 1260|  2.11M|      },

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_18internal8westmere26write_v_u16_11bits_to_utf8EDv2_xRPcS4_t:
   10|  68.1k|                                       const uint16_t one_byte_bitmask) {
   11|       |  // 0b1100_0000_1000_0000
   12|  68.1k|  const __m128i v_c080 = _mm_set1_epi16((int16_t)0xc080);
   13|       |  // 0b0001_1111_0000_0000
   14|  68.1k|  const __m128i v_1f00 = _mm_set1_epi16((int16_t)0x1f00);
   15|       |  // 0b0000_0000_0011_1111
   16|  68.1k|  const __m128i v_003f = _mm_set1_epi16((int16_t)0x003f);
   17|       |
   18|       |  // 1. prepare 2-byte values
   19|       |  // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
   20|       |  // expected output   : [110a|aaaa|10bb|bbbb] x 8
   21|       |
   22|       |  // t0 = [000a|aaaa|bbbb|bb00]
   23|  68.1k|  const __m128i t0 = _mm_slli_epi16(v_u16, 2);
   24|       |  // t1 = [000a|aaaa|0000|0000]
   25|  68.1k|  const __m128i t1 = _mm_and_si128(t0, v_1f00);
   26|       |  // t2 = [0000|0000|00bb|bbbb]
   27|  68.1k|  const __m128i t2 = _mm_and_si128(v_u16, v_003f);
   28|       |  // t3 = [000a|aaaa|00bb|bbbb]
   29|  68.1k|  const __m128i t3 = _mm_or_si128(t1, t2);
   30|       |  // t4 = [110a|aaaa|10bb|bbbb]
   31|  68.1k|  const __m128i t4 = _mm_or_si128(t3, v_c080);
   32|       |
   33|       |  // 2. merge ASCII and 2-byte codewords
   34|  68.1k|  const __m128i utf8_unpacked = _mm_blendv_epi8(t4, v_u16, one_byte_bytemask);
   35|       |
   36|       |  // 3. prepare bitmask for 8-bit lookup
   37|       |  //    one_byte_bitmask = hhggffeeddccbbaa -- the bits are doubled (h - MSB, a
   38|       |  //    - LSB)
   39|  68.1k|  const uint16_t m0 = one_byte_bitmask & 0x5555;      // m0 = 0h0g0f0e0d0c0b0a
   40|  68.1k|  const uint16_t m1 = static_cast<uint16_t>(m0 >> 7); // m1 = 00000000h0g0f0e0
   41|  68.1k|  const uint8_t m2 = static_cast<uint8_t>((m0 | m1) & 0xff); // m2 = hdgcfbea
   42|       |  // 4. pack the bytes
   43|  68.1k|  const uint8_t *row =
   44|  68.1k|      &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0];
   45|  68.1k|  const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
   46|  68.1k|  const __m128i utf8_packed = _mm_shuffle_epi8(utf8_unpacked, shuffle);
   47|       |
   48|       |  // 5. store bytes
   49|  68.1k|  _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   50|       |
   51|       |  // 6. adjust pointers
   52|  68.1k|  utf8_output += row[0];
   53|  68.1k|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_137sse_convert_utf16_to_utf8_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
  285|  2.46M|                                      char *utf8_output) {
  286|  2.46M|  const char16_t *start = buf;
  287|  2.46M|  const char16_t *end = buf + len;
  288|       |
  289|  2.46M|  const __m128i v_0000 = _mm_setzero_si128();
  290|  2.46M|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
  291|  2.46M|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
  292|  2.46M|  const size_t safety_margin =
  293|  2.46M|      12; // to avoid overruns, see issue
  294|       |          // https://github.com/simdutf/simdutf/issues/92
  295|       |
  296|  5.42M|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (296:10): [True: 5.42M, False: 4.21k]
  ------------------
  297|  5.42M|    __m128i in = _mm_loadu_si128((__m128i *)buf);
  298|  5.42M|    if (big_endian) {
  ------------------
  |  Branch (298:9): [Folded, False: 5.42M]
  ------------------
  299|      0|      const __m128i swap =
  300|      0|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  301|      0|      in = _mm_shuffle_epi8(in, swap);
  302|      0|    }
  303|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
  304|  5.42M|    const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80);
  305|  5.42M|    if (_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (305:9): [True: 769k, False: 4.65M]
  ------------------
  306|   769k|      __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
  307|   769k|      if (big_endian) {
  ------------------
  |  Branch (307:11): [Folded, False: 769k]
  ------------------
  308|      0|        const __m128i swap =
  309|      0|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  310|      0|        nextin = _mm_shuffle_epi8(nextin, swap);
  311|      0|      }
  312|   769k|      if (!_mm_testz_si128(nextin, v_ff80)) {
  ------------------
  |  Branch (312:11): [True: 7.96k, False: 761k]
  ------------------
  313|       |        // 1. pack the bytes
  314|       |        // obviously suboptimal.
  315|  7.96k|        const __m128i utf8_packed = _mm_packus_epi16(in, in);
  316|       |        // 2. store (16 bytes)
  317|  7.96k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  318|       |        // 3. adjust pointers
  319|  7.96k|        buf += 8;
  320|  7.96k|        utf8_output += 8;
  321|  7.96k|        in = nextin;
  322|   761k|      } else {
  323|       |        // 1. pack the bytes
  324|       |        // obviously suboptimal.
  325|   761k|        const __m128i utf8_packed = _mm_packus_epi16(in, nextin);
  326|       |        // 2. store (16 bytes)
  327|   761k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  328|       |        // 3. adjust pointers
  329|   761k|        buf += 16;
  330|   761k|        utf8_output += 16;
  331|   761k|        continue; // we are done for this round!
  332|   761k|      }
  333|   769k|    }
  334|       |
  335|       |    // no bits set above 7th bit
  336|  4.66M|    const __m128i one_byte_bytemask =
  337|  4.66M|        _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000);
  338|  4.66M|    const uint16_t one_byte_bitmask =
  339|  4.66M|        static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
  340|       |
  341|       |    // no bits set above 11th bit
  342|  4.66M|    const __m128i one_or_two_bytes_bytemask =
  343|  4.66M|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000);
  344|  4.66M|    const uint16_t one_or_two_bytes_bitmask =
  345|  4.66M|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  346|       |
  347|  4.66M|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (347:9): [True: 49.7k, False: 4.61M]
  ------------------
  348|  49.7k|      internal::westmere::write_v_u16_11bits_to_utf8(
  349|  49.7k|          in, utf8_output, one_byte_bytemask, one_byte_bitmask);
  350|  49.7k|      buf += 8;
  351|  49.7k|      continue;
  352|  49.7k|    }
  353|       |
  354|       |    // 1. Check if there are any surrogate word in the input chunk.
  355|       |    //    We have also deal with situation when there is a surrogate word
  356|       |    //    at the end of a chunk.
  357|  4.61M|    const __m128i surrogates_bytemask =
  358|  4.61M|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800);
  359|       |
  360|       |    // bitmask = 0x0000 if there are no surrogates
  361|       |    //         = 0xc000 if the last word is a surrogate
  362|  4.61M|    const uint16_t surrogates_bitmask =
  363|  4.61M|        static_cast<uint16_t>(_mm_movemask_epi8(surrogates_bytemask));
  364|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
  365|       |    // However, it is likely an uncommon occurrence.
  366|  4.61M|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (366:9): [True: 2.14M, False: 2.46M]
  ------------------
  367|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  368|  2.14M|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  369|  2.14M|                                              0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
  370|       |
  371|       |      /* In this branch we handle three cases:
  372|       |         1. [0000|0000|0ccc|cccc] => [0ccc|cccc]                           -
  373|       |        single UFT-8 byte
  374|       |         2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc]              - two
  375|       |        UTF-8 bytes
  376|       |         3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] -
  377|       |        three UTF-8 bytes
  378|       |
  379|       |        We expand the input word (16-bit) into two code units (32-bit), thus
  380|       |        we have room for four bytes. However, we need five distinct bit
  381|       |        layouts. Note that the last byte in cases #2 and #3 is the same.
  382|       |
  383|       |        We precompute byte 1 for case #1 and the common byte for cases #2 & #3
  384|       |        in register t2.
  385|       |
  386|       |        We precompute byte 1 for case #3 and -- **conditionally** -- precompute
  387|       |        either byte 1 for case #2 or byte 2 for case #3. Note that they
  388|       |        differ by exactly one bit.
  389|       |
  390|       |        Finally from these two code units we build proper UTF-8 sequence, taking
  391|       |        into account the case (i.e, the number of bytes to write).
  392|       |      */
  393|       |      /**
  394|       |       * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
  395|       |       * t2 => [0ccc|cccc] [10cc|cccc]
  396|       |       * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
  397|       |       */
  398|  2.14M|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  399|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  400|  2.14M|      const __m128i t0 = _mm_shuffle_epi8(in, dup_even);
  401|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  402|  2.14M|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  398|  2.14M|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  403|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  404|  2.14M|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  398|  2.14M|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  405|       |
  406|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  407|  2.14M|      const __m128i s0 = _mm_srli_epi16(in, 4);
  408|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  409|  2.14M|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  398|  2.14M|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  410|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  411|  2.14M|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  398|  2.14M|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  412|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  413|  2.14M|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  398|  2.14M|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  414|  2.14M|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  415|  2.14M|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  398|  2.14M|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  416|  2.14M|      const __m128i s4 = _mm_xor_si128(s3, m0);
  417|  2.14M|#undef simdutf_vec
  418|       |
  419|       |      // 4. expand code units 16-bit => 32-bit
  420|  2.14M|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  421|  2.14M|      const __m128i out1 = _mm_unpackhi_epi16(t2, s4);
  422|       |
  423|       |      // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
  424|  2.14M|      const uint16_t mask =
  425|  2.14M|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  426|  2.14M|      if (mask == 0) {
  ------------------
  |  Branch (426:11): [True: 1.94M, False: 198k]
  ------------------
  427|       |        // We only have three-byte code units. Use fast path.
  428|  1.94M|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  429|  1.94M|                                              15, 13, -1, -1, -1, -1);
  430|  1.94M|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  431|  1.94M|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  432|  1.94M|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  433|  1.94M|        utf8_output += 12;
  434|  1.94M|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  435|  1.94M|        utf8_output += 12;
  436|  1.94M|        buf += 8;
  437|  1.94M|        continue;
  438|  1.94M|      }
  439|   198k|      const uint8_t mask0 = uint8_t(mask);
  440|       |
  441|   198k|      const uint8_t *row0 =
  442|   198k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  443|   198k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  444|   198k|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  445|       |
  446|   198k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  447|       |
  448|   198k|      const uint8_t *row1 =
  449|   198k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  450|   198k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  451|   198k|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  452|       |
  453|   198k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  454|   198k|      utf8_output += row0[0];
  455|   198k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  456|   198k|      utf8_output += row1[0];
  457|       |
  458|   198k|      buf += 8;
  459|       |      // surrogate pair(s) in a register
  460|  2.46M|    } else {
  461|       |      // Let us do a scalar fallback.
  462|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
  463|       |      // in the presence of surrogate pairs may require non-trivial tables.
  464|  2.46M|      size_t forward = 15;
  465|  2.46M|      size_t k = 0;
  466|  2.46M|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (466:11): [True: 0, False: 2.46M]
  ------------------
  467|      0|        forward = size_t(end - buf - 1);
  468|      0|      }
  469|  2.94M|      for (; k < forward; k++) {
  ------------------
  |  Branch (469:14): [True: 2.94M, False: 2.52k]
  ------------------
  470|  2.94M|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  471|  2.94M|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (471:13): [True: 27.7k, False: 2.91M]
  ------------------
  472|  27.7k|          *utf8_output++ = char(word);
  473|  2.91M|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (473:20): [True: 17.7k, False: 2.89M]
  ------------------
  474|  17.7k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  475|  17.7k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  476|  2.89M|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (476:20): [True: 399k, False: 2.49M]
  ------------------
  477|   399k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  478|   399k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  479|   399k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  480|  2.49M|        } else {
  481|       |          // must be a surrogate pair
  482|  2.49M|          uint16_t diff = uint16_t(word - 0xD800);
  483|  2.49M|          uint16_t next_word =
  484|  2.49M|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  485|  2.49M|          k++;
  486|  2.49M|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  487|  2.49M|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (487:15): [True: 2.46M, False: 31.1k]
  ------------------
  488|  2.46M|            return std::make_pair(
  489|  2.46M|                result(error_code::SURROGATE, buf - start + k - 1),
  490|  2.46M|                utf8_output);
  491|  2.46M|          }
  492|  31.1k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  493|  31.1k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  494|  31.1k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  495|  31.1k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  496|  31.1k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  497|  31.1k|        }
  498|  2.94M|      }
  499|  2.52k|      buf += k;
  500|  2.52k|    }
  501|  4.61M|  } // while
  502|       |
  503|  4.21k|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  504|  2.46M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_137sse_convert_utf16_to_utf8_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
  285|  2.11M|                                      char *utf8_output) {
  286|  2.11M|  const char16_t *start = buf;
  287|  2.11M|  const char16_t *end = buf + len;
  288|       |
  289|  2.11M|  const __m128i v_0000 = _mm_setzero_si128();
  290|  2.11M|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
  291|  2.11M|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
  292|  2.11M|  const size_t safety_margin =
  293|  2.11M|      12; // to avoid overruns, see issue
  294|       |          // https://github.com/simdutf/simdutf/issues/92
  295|       |
  296|  4.27M|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (296:10): [True: 4.26M, False: 4.40k]
  ------------------
  297|  4.26M|    __m128i in = _mm_loadu_si128((__m128i *)buf);
  298|  4.26M|    if (big_endian) {
  ------------------
  |  Branch (298:9): [True: 4.26M, Folded]
  ------------------
  299|  4.26M|      const __m128i swap =
  300|  4.26M|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  301|  4.26M|      in = _mm_shuffle_epi8(in, swap);
  302|  4.26M|    }
  303|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
  304|  4.26M|    const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80);
  305|  4.26M|    if (_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (305:9): [True: 747k, False: 3.52M]
  ------------------
  306|   747k|      __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
  307|   747k|      if (big_endian) {
  ------------------
  |  Branch (307:11): [True: 747k, Folded]
  ------------------
  308|   747k|        const __m128i swap =
  309|   747k|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  310|   747k|        nextin = _mm_shuffle_epi8(nextin, swap);
  311|   747k|      }
  312|   747k|      if (!_mm_testz_si128(nextin, v_ff80)) {
  ------------------
  |  Branch (312:11): [True: 24.5k, False: 722k]
  ------------------
  313|       |        // 1. pack the bytes
  314|       |        // obviously suboptimal.
  315|  24.5k|        const __m128i utf8_packed = _mm_packus_epi16(in, in);
  316|       |        // 2. store (16 bytes)
  317|  24.5k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  318|       |        // 3. adjust pointers
  319|  24.5k|        buf += 8;
  320|  24.5k|        utf8_output += 8;
  321|  24.5k|        in = nextin;
  322|   722k|      } else {
  323|       |        // 1. pack the bytes
  324|       |        // obviously suboptimal.
  325|   722k|        const __m128i utf8_packed = _mm_packus_epi16(in, nextin);
  326|       |        // 2. store (16 bytes)
  327|   722k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  328|       |        // 3. adjust pointers
  329|   722k|        buf += 16;
  330|   722k|        utf8_output += 16;
  331|   722k|        continue; // we are done for this round!
  332|   722k|      }
  333|   747k|    }
  334|       |
  335|       |    // no bits set above 7th bit
  336|  3.54M|    const __m128i one_byte_bytemask =
  337|  3.54M|        _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000);
  338|  3.54M|    const uint16_t one_byte_bitmask =
  339|  3.54M|        static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
  340|       |
  341|       |    // no bits set above 11th bit
  342|  3.54M|    const __m128i one_or_two_bytes_bytemask =
  343|  3.54M|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000);
  344|  3.54M|    const uint16_t one_or_two_bytes_bitmask =
  345|  3.54M|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  346|       |
  347|  3.54M|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (347:9): [True: 18.3k, False: 3.52M]
  ------------------
  348|  18.3k|      internal::westmere::write_v_u16_11bits_to_utf8(
  349|  18.3k|          in, utf8_output, one_byte_bytemask, one_byte_bitmask);
  350|  18.3k|      buf += 8;
  351|  18.3k|      continue;
  352|  18.3k|    }
  353|       |
  354|       |    // 1. Check if there are any surrogate word in the input chunk.
  355|       |    //    We have also deal with situation when there is a surrogate word
  356|       |    //    at the end of a chunk.
  357|  3.52M|    const __m128i surrogates_bytemask =
  358|  3.52M|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800);
  359|       |
  360|       |    // bitmask = 0x0000 if there are no surrogates
  361|       |    //         = 0xc000 if the last word is a surrogate
  362|  3.52M|    const uint16_t surrogates_bitmask =
  363|  3.52M|        static_cast<uint16_t>(_mm_movemask_epi8(surrogates_bytemask));
  364|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
  365|       |    // However, it is likely an uncommon occurrence.
  366|  3.52M|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (366:9): [True: 1.41M, False: 2.11M]
  ------------------
  367|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  368|  1.41M|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  369|  1.41M|                                              0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
  370|       |
  371|       |      /* In this branch we handle three cases:
  372|       |         1. [0000|0000|0ccc|cccc] => [0ccc|cccc]                           -
  373|       |        single UFT-8 byte
  374|       |         2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc]              - two
  375|       |        UTF-8 bytes
  376|       |         3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] -
  377|       |        three UTF-8 bytes
  378|       |
  379|       |        We expand the input word (16-bit) into two code units (32-bit), thus
  380|       |        we have room for four bytes. However, we need five distinct bit
  381|       |        layouts. Note that the last byte in cases #2 and #3 is the same.
  382|       |
  383|       |        We precompute byte 1 for case #1 and the common byte for cases #2 & #3
  384|       |        in register t2.
  385|       |
  386|       |        We precompute byte 1 for case #3 and -- **conditionally** -- precompute
  387|       |        either byte 1 for case #2 or byte 2 for case #3. Note that they
  388|       |        differ by exactly one bit.
  389|       |
  390|       |        Finally from these two code units we build proper UTF-8 sequence, taking
  391|       |        into account the case (i.e, the number of bytes to write).
  392|       |      */
  393|       |      /**
  394|       |       * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
  395|       |       * t2 => [0ccc|cccc] [10cc|cccc]
  396|       |       * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
  397|       |       */
  398|  1.41M|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  399|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  400|  1.41M|      const __m128i t0 = _mm_shuffle_epi8(in, dup_even);
  401|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  402|  1.41M|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  398|  1.41M|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  403|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  404|  1.41M|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  398|  1.41M|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  405|       |
  406|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  407|  1.41M|      const __m128i s0 = _mm_srli_epi16(in, 4);
  408|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  409|  1.41M|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  398|  1.41M|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  410|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  411|  1.41M|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  398|  1.41M|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  412|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  413|  1.41M|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  398|  1.41M|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  414|  1.41M|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  415|  1.41M|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  398|  1.41M|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  416|  1.41M|      const __m128i s4 = _mm_xor_si128(s3, m0);
  417|  1.41M|#undef simdutf_vec
  418|       |
  419|       |      // 4. expand code units 16-bit => 32-bit
  420|  1.41M|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  421|  1.41M|      const __m128i out1 = _mm_unpackhi_epi16(t2, s4);
  422|       |
  423|       |      // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
  424|  1.41M|      const uint16_t mask =
  425|  1.41M|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  426|  1.41M|      if (mask == 0) {
  ------------------
  |  Branch (426:11): [True: 1.23M, False: 179k]
  ------------------
  427|       |        // We only have three-byte code units. Use fast path.
  428|  1.23M|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  429|  1.23M|                                              15, 13, -1, -1, -1, -1);
  430|  1.23M|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  431|  1.23M|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  432|  1.23M|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  433|  1.23M|        utf8_output += 12;
  434|  1.23M|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  435|  1.23M|        utf8_output += 12;
  436|  1.23M|        buf += 8;
  437|  1.23M|        continue;
  438|  1.23M|      }
  439|   179k|      const uint8_t mask0 = uint8_t(mask);
  440|       |
  441|   179k|      const uint8_t *row0 =
  442|   179k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  443|   179k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  444|   179k|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  445|       |
  446|   179k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  447|       |
  448|   179k|      const uint8_t *row1 =
  449|   179k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  450|   179k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  451|   179k|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  452|       |
  453|   179k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  454|   179k|      utf8_output += row0[0];
  455|   179k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  456|   179k|      utf8_output += row1[0];
  457|       |
  458|   179k|      buf += 8;
  459|       |      // surrogate pair(s) in a register
  460|  2.11M|    } else {
  461|       |      // Let us do a scalar fallback.
  462|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
  463|       |      // in the presence of surrogate pairs may require non-trivial tables.
  464|  2.11M|      size_t forward = 15;
  465|  2.11M|      size_t k = 0;
  466|  2.11M|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (466:11): [True: 0, False: 2.11M]
  ------------------
  467|      0|        forward = size_t(end - buf - 1);
  468|      0|      }
  469|  2.85M|      for (; k < forward; k++) {
  ------------------
  |  Branch (469:14): [True: 2.85M, False: 1.94k]
  ------------------
  470|  2.85M|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  471|  2.85M|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (471:13): [True: 46.9k, False: 2.81M]
  ------------------
  472|  46.9k|          *utf8_output++ = char(word);
  473|  2.81M|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (473:20): [True: 38.0k, False: 2.77M]
  ------------------
  474|  38.0k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  475|  38.0k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  476|  2.77M|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (476:20): [True: 593k, False: 2.17M]
  ------------------
  477|   593k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  478|   593k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  479|   593k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  480|  2.17M|        } else {
  481|       |          // must be a surrogate pair
  482|  2.17M|          uint16_t diff = uint16_t(word - 0xD800);
  483|  2.17M|          uint16_t next_word =
  484|  2.17M|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  485|  2.17M|          k++;
  486|  2.17M|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  487|  2.17M|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (487:15): [True: 2.11M, False: 64.0k]
  ------------------
  488|  2.11M|            return std::make_pair(
  489|  2.11M|                result(error_code::SURROGATE, buf - start + k - 1),
  490|  2.11M|                utf8_output);
  491|  2.11M|          }
  492|  64.0k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  493|  64.0k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  494|  64.0k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  495|  64.0k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  496|  64.0k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  497|  64.0k|        }
  498|  2.85M|      }
  499|  1.94k|      buf += k;
  500|  1.94k|    }
  501|  3.52M|  } // while
  502|       |
  503|  4.40k|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  504|  2.11M|}

