_Z19test_latin1_to_utf8NSt3__14spanIKhLm18446744073709551615EEEm:
   10|    733|                         std::size_t output_size) {
   11|    733|  std::vector<char> output(output_size);
   12|    733|  const auto written_bytes_safe =
   13|    733|      simdutf::convert_latin1_to_utf8_safe(input_bytes, output);
   14|    733|  if (written_bytes_safe > output_size) {
  ------------------
  |  Branch (14:7): [True: 0, False: 733]
  ------------------
   15|      0|    std::abort();
   16|      0|  }
   17|    733|  const auto needed_size = simdutf::utf8_length_from_latin1(input_bytes);
   18|    733|  std::vector<char> reference(needed_size);
   19|    733|  const auto written_bytes_unsafe =
   20|    733|      simdutf::convert_latin1_to_utf8(input_bytes, reference);
   21|    733|  if (written_bytes_unsafe != needed_size) {
  ------------------
  |  Branch (21:7): [True: 0, False: 733]
  ------------------
   22|      0|    std::abort();
   23|      0|  }
   24|    733|  if (written_bytes_safe > needed_size) {
  ------------------
  |  Branch (24:7): [True: 0, False: 733]
  ------------------
   25|       |    // convert_latin1_to_utf8_safe wrote more output buffer than the unsafe
   26|       |    // version needed!
   27|      0|    std::abort();
   28|      0|  }
   29|       |  // ensure output is equal to the beginning of reference
   30|    733|  if (!std::ranges::equal(
  ------------------
  |  Branch (30:7): [True: 0, False: 733]
  ------------------
   31|    733|          std::span(output).subspan(0, written_bytes_safe),
   32|    733|          std::span(reference).subspan(0, written_bytes_safe))) {
   33|      0|    std::abort();
   34|      0|  }
   35|    733|}
_Z18test_utf16_to_utf8NSt3__14spanIKDsLm18446744073709551615EEEm:
   38|  1.00k|                        std::size_t output_size) {
   39|  1.00k|  std::vector<char> output(output_size);
   40|  1.00k|  const auto written_bytes_safe =
   41|  1.00k|      simdutf::convert_utf16_to_utf8_safe(input, output);
   42|  1.00k|  if (written_bytes_safe > output_size) {
  ------------------
  |  Branch (42:7): [True: 0, False: 1.00k]
  ------------------
   43|      0|    std::abort();
   44|      0|  }
   45|       |  // result is implementation defined in case of garbage input
   46|  1.00k|  const auto unreliable_needed_size = simdutf::utf8_length_from_utf16(input);
   47|  1.00k|  std::vector<char> reference(unreliable_needed_size);
   48|  1.00k|  const auto written_bytes_unsafe =
   49|  1.00k|      simdutf::convert_utf16_to_utf8(input, reference);
   50|       |
   51|       |  // ensure output is equal to the beginning of reference
   52|  1.00k|  const auto Ncompare =
   53|  1.00k|      simdutf::detail::min(written_bytes_safe, written_bytes_unsafe);
   54|  1.00k|  const auto matches =
   55|  1.00k|      std::ranges::equal(std::span(output).subspan(0, Ncompare),
   56|  1.00k|                         std::span(reference).subspan(0, Ncompare));
   57|  1.00k|  assert(matches);
  ------------------
  |  Branch (57:3): [True: 1.00k, False: 0]
  ------------------
   58|  1.00k|  if (!matches) {
  ------------------
  |  Branch (58:7): [True: 0, False: 1.00k]
  ------------------
   59|      0|    std::abort();
   60|      0|  }
   61|  1.00k|}
LLVMFuzzerTestOneInput:
   80|  1.74k|extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   81|       |
   82|  1.74k|  if (size < 4) {
  ------------------
  |  Branch (82:7): [True: 2, False: 1.73k]
  ------------------
   83|      2|    return 0;
   84|      2|  }
   85|       |
   86|  1.73k|  const auto action = data[0] & 0x1;
   87|  1.73k|  const auto output_size = (data[1] << 8 | data[2]);
   88|  1.73k|  const auto implementation_index = data[3] & 0b0111;
   89|  1.73k|  data += 4;
   90|  1.73k|  size -= 4;
   91|       |
   92|  1.73k|  const std::span<const uint8_t> input_bytes{data, data + size};
   93|       |
   94|  1.73k|  select_implementation(implementation_index);
   95|       |
   96|  1.73k|  switch (action) {
  ------------------
  |  Branch (96:11): [True: 1.73k, False: 0]
  ------------------
   97|    733|  case 0:
  ------------------
  |  Branch (97:3): [True: 733, False: 1.00k]
  ------------------
   98|    733|    test_latin1_to_utf8(input_bytes, output_size);
   99|    733|    break;
  100|  1.00k|  case 1: {
  ------------------
  |  Branch (100:3): [True: 1.00k, False: 733]
  ------------------
  101|  1.00k|    const auto* ptr = reinterpret_cast<const char16_t*>(input_bytes.data());
  102|  1.00k|    test_utf16_to_utf8(std::span(ptr, ptr + input_bytes.size() / 2),
  103|  1.00k|                       output_size);
  104|  1.00k|  } break;
  105|  1.73k|  }
  106|       |
  107|  1.73k|  return 0;
  108|  1.73k|}
_Z21select_implementationIiEvT_:
   63|  1.73k|void select_implementation(auto index) {
   64|  1.73k|  static const auto implementations = []() {
   65|  1.73k|    const auto list = simdutf::get_available_implementations();
   66|  1.73k|    using Impl = std::decay_t<decltype(*list.begin())>;
   67|  1.73k|    std::vector<Impl> ret;
   68|  1.73k|    for (auto& e : list) {
   69|  1.73k|      if (e->supported_by_runtime_system()) {
   70|  1.73k|        ret.push_back(e);
   71|  1.73k|      }
   72|  1.73k|    }
   73|  1.73k|    return ret;
   74|  1.73k|  }();
   75|  1.73k|  assert(!implementations.empty());
  ------------------
  |  Branch (75:3): [True: 1.73k, False: 0]
  ------------------
   76|  1.73k|  simdutf::get_active_implementation() =
   77|  1.73k|      implementations.at(index % implementations.size());
   78|  1.73k|}
_ZZ21select_implementationIiEvT_ENKUlvE_clEv:
   64|      1|  static const auto implementations = []() {
   65|      1|    const auto list = simdutf::get_available_implementations();
   66|      1|    using Impl = std::decay_t<decltype(*list.begin())>;
   67|      1|    std::vector<Impl> ret;
   68|      4|    for (auto& e : list) {
  ------------------
  |  Branch (68:18): [True: 4, False: 1]
  ------------------
   69|      4|      if (e->supported_by_runtime_system()) {
  ------------------
  |  Branch (69:11): [True: 3, False: 1]
  ------------------
   70|      3|        ret.push_back(e);
   71|      3|      }
   72|      4|    }
   73|      1|    return ret;
   74|      1|  }();

_ZN7simdutf11full_resultC2ENS_10error_codeEmm:
  111|    830|      : error{err}, input_count{pos_in}, output_count{pos_out} {}

_ZNK7simdutf14implementation25required_instruction_setsEv:
 5172|      4|  virtual uint32_t required_instruction_sets() const {
 5173|      4|    return _required_instruction_sets;
 5174|      4|  }
_ZN7simdutf14implementationC2EPKcS2_j:
 7058|      5|      : _name(name), _description(description),
 7059|      5|        _required_instruction_sets(required_instruction_sets) {}
_ZN7simdutf8internal29available_implementation_listC2Ev:
 7090|      1|  simdutf_really_inline available_implementation_list() {}
simdutf.cpp:_ZN7simdutf6detail12_GLOBAL__N_13minEmm:
   59|  3.98k|constexpr std::size_t min(std::size_t a, std::size_t b) {
   60|  3.98k|  return a < b ? a : b;
  ------------------
  |  Branch (60:10): [True: 1.41k, False: 2.57k]
  ------------------
   61|  3.98k|}
_ZN7simdutf8internal10atomic_ptrIKNS_14implementationEEC2EPS3_:
 7138|      1|  atomic_ptr(T *_ptr) : ptr{_ptr} {}
_ZN7simdutf8internal10atomic_ptrIKNS_14implementationEEptEv:
 7160|  5.90k|  T *operator->() { return ptr.load(); }

simdutf.cpp:_ZN7simdutf8internalL30detect_supported_architecturesEv:
  236|      4|static inline uint32_t detect_supported_architectures() {
  237|      4|  uint32_t eax;
  238|      4|  uint32_t ebx = 0;
  239|      4|  uint32_t ecx = 0;
  240|      4|  uint32_t edx = 0;
  241|      4|  uint32_t host_isa = 0x0;
  242|       |
  243|       |  // EBX for EAX=0x1
  244|      4|  eax = 0x1;
  245|      4|  cpuid(&eax, &ebx, &ecx, &edx);
  246|       |
  247|      4|  if (ecx & cpuid_bit::sse42) {
  ------------------
  |  Branch (247:7): [True: 4, False: 0]
  ------------------
  248|      4|    host_isa |= instruction_set::SSE42;
  249|      4|  }
  250|       |
  251|      4|  if (ecx & cpuid_bit::pclmulqdq) {
  ------------------
  |  Branch (251:7): [True: 4, False: 0]
  ------------------
  252|      4|    host_isa |= instruction_set::PCLMULQDQ;
  253|      4|  }
  254|       |
  255|      4|  if ((ecx & cpuid_bit::osxsave) != cpuid_bit::osxsave) {
  ------------------
  |  Branch (255:7): [True: 0, False: 4]
  ------------------
  256|      0|    return host_isa;
  257|      0|  }
  258|       |
  259|       |  // xgetbv for checking if the OS saves registers
  260|      4|  uint64_t xcr0 = xgetbv();
  261|       |
  262|      4|  if ((xcr0 & cpuid_bit::xcr0_bit::avx256_saved) == 0) {
  ------------------
  |  Branch (262:7): [True: 0, False: 4]
  ------------------
  263|      0|    return host_isa;
  264|      0|  }
  265|       |  // ECX for EAX=0x7
  266|      4|  eax = 0x7;
  267|      4|  ecx = 0x0; // Sub-leaf = 0
  268|      4|  cpuid(&eax, &ebx, &ecx, &edx);
  269|      4|  if (ebx & cpuid_bit::ebx::avx2) {
  ------------------
  |  Branch (269:7): [True: 4, False: 0]
  ------------------
  270|      4|    host_isa |= instruction_set::AVX2;
  271|      4|  }
  272|      4|  if (ebx & cpuid_bit::ebx::bmi1) {
  ------------------
  |  Branch (272:7): [True: 4, False: 0]
  ------------------
  273|      4|    host_isa |= instruction_set::BMI1;
  274|      4|  }
  275|      4|  if (ebx & cpuid_bit::ebx::bmi2) {
  ------------------
  |  Branch (275:7): [True: 4, False: 0]
  ------------------
  276|      4|    host_isa |= instruction_set::BMI2;
  277|      4|  }
  278|      4|  if (!((xcr0 & cpuid_bit::xcr0_bit::avx512_saved) ==
  ------------------
  |  Branch (278:7): [True: 4, False: 0]
  ------------------
  279|      4|        cpuid_bit::xcr0_bit::avx512_saved)) {
  280|      4|    return host_isa;
  281|      4|  }
  282|      0|  if (ebx & cpuid_bit::ebx::avx512f) {
  ------------------
  |  Branch (282:7): [True: 0, False: 0]
  ------------------
  283|      0|    host_isa |= instruction_set::AVX512F;
  284|      0|  }
  285|      0|  if (ebx & cpuid_bit::ebx::avx512bw) {
  ------------------
  |  Branch (285:7): [True: 0, False: 0]
  ------------------
  286|      0|    host_isa |= instruction_set::AVX512BW;
  287|      0|  }
  288|      0|  if (ebx & cpuid_bit::ebx::avx512cd) {
  ------------------
  |  Branch (288:7): [True: 0, False: 0]
  ------------------
  289|      0|    host_isa |= instruction_set::AVX512CD;
  290|      0|  }
  291|      0|  if (ebx & cpuid_bit::ebx::avx512dq) {
  ------------------
  |  Branch (291:7): [True: 0, False: 0]
  ------------------
  292|      0|    host_isa |= instruction_set::AVX512DQ;
  293|      0|  }
  294|      0|  if (ebx & cpuid_bit::ebx::avx512vl) {
  ------------------
  |  Branch (294:7): [True: 0, False: 0]
  ------------------
  295|      0|    host_isa |= instruction_set::AVX512VL;
  296|      0|  }
  297|      0|  if (ecx & cpuid_bit::ecx::avx512vbmi2) {
  ------------------
  |  Branch (297:7): [True: 0, False: 0]
  ------------------
  298|      0|    host_isa |= instruction_set::AVX512VBMI2;
  299|      0|  }
  300|      0|  if (ecx & cpuid_bit::ecx::avx512vpopcnt) {
  ------------------
  |  Branch (300:7): [True: 0, False: 0]
  ------------------
  301|      0|    host_isa |= instruction_set::AVX512VPOPCNTDQ;
  302|      0|  }
  303|      0|  return host_isa;
  304|      4|}
simdutf.cpp:_ZN7simdutf8internalL5cpuidEPjS1_S1_S1_:
  202|      8|                         uint32_t *edx) {
  203|       |  #if defined(_MSC_VER)
  204|       |  int cpu_info[4];
  205|       |  __cpuidex(cpu_info, *eax, *ecx);
  206|       |  *eax = cpu_info[0];
  207|       |  *ebx = cpu_info[1];
  208|       |  *ecx = cpu_info[2];
  209|       |  *edx = cpu_info[3];
  210|       |  #elif (defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)) ||         \
  211|       |      defined(__FILC__)
  212|       |  uint32_t level = *eax;
  213|       |  __get_cpuid(level, eax, ebx, ecx, edx);
  214|       |  #else
  215|      8|  uint32_t a = *eax, b, c = *ecx, d;
  216|      8|  asm volatile("cpuid\n\t" : "+a"(a), "=b"(b), "+c"(c), "=d"(d));
  217|      8|  *eax = a;
  218|      8|  *ebx = b;
  219|      8|  *ecx = c;
  220|      8|  *edx = d;
  221|      8|  #endif
  222|      8|}
simdutf.cpp:_ZN7simdutf8internalL6xgetbvEv:
  224|      4|static inline uint64_t xgetbv() {
  225|       |  #if defined(_MSC_VER)
  226|       |  return _xgetbv(0);
  227|       |  #elif defined(__FILC__)
  228|       |  return zxgetbv();
  229|       |  #else
  230|      4|  uint32_t xcr0_lo, xcr0_hi;
  231|      4|  asm volatile("xgetbv\n\t" : "=a"(xcr0_lo), "=d"(xcr0_hi) : "c"(0));
  232|      4|  return xcr0_lo | ((uint64_t)xcr0_hi << 32);
  233|      4|  #endif
  234|      4|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_16latin123utf8_length_from_latin1EPKcm:
   10|    557|                                                     size_t len) {
   11|    557|  const uint8_t *c = reinterpret_cast<const uint8_t *>(buf);
   12|    557|  size_t answer = 0;
   13|  4.41k|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (13:22): [True: 3.85k, False: 557]
  ------------------
   14|  3.85k|    if ((c[i] >> 7)) {
  ------------------
  |  Branch (14:9): [True: 1.29k, False: 2.56k]
  ------------------
   15|  1.29k|      answer++;
   16|  1.29k|    }
   17|  3.85k|  }
   18|    557|  return answer + len;
   19|    557|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf812convert_safeEPKcmPcm:
   72|    733|                           size_t utf8_len) {
   73|    733|  const unsigned char *data = reinterpret_cast<const unsigned char *>(buf);
   74|    733|  size_t pos = 0;
   75|    733|  size_t skip_pos = 0;
   76|    733|  size_t utf8_pos = 0;
   77|  4.10k|  while (pos < len && utf8_pos < utf8_len) {
  ------------------
  |  Branch (77:10): [True: 3.68k, False: 423]
  |  Branch (77:23): [True: 3.41k, False: 270]
  ------------------
   78|       |    // try to convert the next block of 16 ASCII bytes
   79|  3.41k|    if (pos >= skip_pos && pos + 16 <= len &&
  ------------------
  |  Branch (79:9): [True: 1.90k, False: 1.50k]
  |  Branch (79:28): [True: 1.30k, False: 601]
  ------------------
   80|  1.30k|        utf8_pos + 16 <= utf8_len) { // if it is safe to read 16 more bytes,
  ------------------
  |  Branch (80:9): [True: 268, False: 1.04k]
  ------------------
   81|       |                                     // check that they are ascii
   82|    268|      uint64_t v1;
   83|    268|      ::memcpy(&v1, data + pos, sizeof(uint64_t));
   84|    268|      uint64_t v2;
   85|    268|      ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   86|    268|      uint64_t v{v1 |
   87|    268|                 v2}; // We are only interested in these bits: 1000 1000 1000
   88|       |                      // 1000, so it makes sense to concatenate everything
   89|    268|      if ((v & 0x8080808080808080) ==
  ------------------
  |  Branch (89:11): [True: 166, False: 102]
  ------------------
   90|    268|          0) { // if NONE of these are set, e.g. all of them are zero, then
   91|       |               // everything is ASCII
   92|    166|        ::memcpy(utf8_output + utf8_pos, buf + pos, 16);
   93|    166|        utf8_pos += 16;
   94|    166|        pos += 16;
   95|    166|      } else {
   96|       |        // At least one of the next 16 bytes are not ASCII, we will process them
   97|       |        // one by one
   98|    102|        skip_pos = pos + 16;
   99|    102|      }
  100|  3.14k|    } else {
  101|  3.14k|      const auto byte = data[pos];
  102|  3.14k|      if ((byte & 0x80) == 0) { // if ASCII
  ------------------
  |  Branch (102:11): [True: 2.18k, False: 954]
  ------------------
  103|       |        // will generate one UTF-8 bytes
  104|  2.18k|        utf8_output[utf8_pos++] = char(byte);
  105|  2.18k|        pos++;
  106|  2.18k|      } else if (utf8_pos + 2 <= utf8_len) {
  ------------------
  |  Branch (106:18): [True: 914, False: 40]
  ------------------
  107|       |        // will generate two UTF-8 bytes
  108|    914|        utf8_output[utf8_pos++] = char((byte >> 6) | 0b11000000);
  109|    914|        utf8_output[utf8_pos++] = char((byte & 0b111111) | 0b10000000);
  110|    914|        pos++;
  111|    914|      } else {
  112|     40|        break;
  113|     40|      }
  114|  3.14k|    }
  115|  3.41k|  }
  116|    733|  return utf8_pos;
  117|    733|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf87convertEPKcmPc:
   66|  1.98k|                                     char *utf8_output) {
   67|  1.98k|  return convert(reinterpret_cast<const unsigned char *>(buf), len,
   68|  1.98k|                 utf8_output);
   69|  1.98k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf87convertIPKhPcQaasr7simdutf6detailE22indexes_into_byte_likeIT_Esr7simdutf6detailE26index_assignable_from_charIT0_EEEmS7_mS8_:
   17|  1.98k|                                   OutputPtr utf8_output) {
   18|       |  // const unsigned char *data = reinterpret_cast<const unsigned char *>(buf);
   19|  1.98k|  size_t pos = 0;
   20|  1.98k|  size_t utf8_pos = 0;
   21|       |
   22|  5.02M|  while (pos < len) {
  ------------------
  |  Branch (22:10): [True: 5.02M, False: 1.98k]
  ------------------
   23|       |#if SIMDUTF_CPLUSPLUS23
   24|       |    if !consteval
   25|       |#endif
   26|  5.02M|    {
   27|       |      // try to convert the next block of 16 ASCII bytes
   28|  5.02M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (28:11): [True: 5.00M, False: 18.4k]
  ------------------
   29|       |                             // they are ascii
   30|  5.00M|        uint64_t v1;
   31|  5.00M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
   32|  5.00M|        uint64_t v2;
   33|  5.00M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   34|  5.00M|        uint64_t v{v1 |
   35|  5.00M|                   v2}; // We are only interested in these bits: 1000 1000 1000
   36|       |                        // 1000, so it makes sense to concatenate everything
   37|  5.00M|        if ((v & 0x8080808080808080) ==
  ------------------
  |  Branch (37:13): [True: 1.04M, False: 3.95M]
  ------------------
   38|  5.00M|            0) { // if NONE of these are set, e.g. all of them are zero, then
   39|       |                 // everything is ASCII
   40|  1.04M|          size_t final_pos = pos + 16;
   41|  17.7M|          while (pos < final_pos) {
  ------------------
  |  Branch (41:18): [True: 16.6M, False: 1.04M]
  ------------------
   42|  16.6M|            utf8_output[utf8_pos++] = char(data[pos]);
   43|  16.6M|            pos++;
   44|  16.6M|          }
   45|  1.04M|          continue;
   46|  1.04M|        }
   47|  5.00M|      } // if (pos + 16 <= len)
   48|  5.02M|    } // !consteval scope
   49|       |
   50|  3.97M|    unsigned char byte = data[pos];
   51|  3.97M|    if ((byte & 0x80) == 0) { // if ASCII
  ------------------
  |  Branch (51:9): [True: 735k, False: 3.24M]
  ------------------
   52|       |      // will generate one UTF-8 bytes
   53|   735k|      utf8_output[utf8_pos++] = char(byte);
   54|   735k|      pos++;
   55|  3.24M|    } else {
   56|       |      // will generate two UTF-8 bytes
   57|  3.24M|      utf8_output[utf8_pos++] = char((byte >> 6) | 0b11000000);
   58|  3.24M|      utf8_output[utf8_pos++] = char((byte & 0b111111) | 0b10000000);
   59|  3.24M|      pos++;
   60|  3.24M|    }
   61|  3.97M|  } // while
   62|  1.98k|  return utf8_pos;
   63|  1.98k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf823utf8_length_from_latin1IPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEEmS6_m:
  152|    176|utf8_length_from_latin1(InputPtr input, size_t length) noexcept {
  153|    176|  size_t answer = length;
  154|    176|  size_t i = 0;
  155|       |
  156|       |#if SIMDUTF_CPLUSPLUS23
  157|       |  if !consteval
  158|       |#endif
  159|    176|  {
  160|    176|    auto pop = [](uint64_t v) {
  161|    176|      return (size_t)(((v >> 7) & UINT64_C(0x0101010101010101)) *
  162|    176|                          UINT64_C(0x0101010101010101) >>
  163|    176|                      56);
  164|    176|    };
  165|   626k|    for (; i + 32 <= length; i += 32) {
  ------------------
  |  Branch (165:12): [True: 625k, False: 176]
  ------------------
  166|   625k|      uint64_t v;
  167|   625k|      memcpy(&v, input + i, 8);
  168|   625k|      answer += pop(v);
  169|   625k|      memcpy(&v, input + i + 8, sizeof(v));
  170|   625k|      answer += pop(v);
  171|   625k|      memcpy(&v, input + i + 16, sizeof(v));
  172|   625k|      answer += pop(v);
  173|   625k|      memcpy(&v, input + i + 24, sizeof(v));
  174|   625k|      answer += pop(v);
  175|   625k|    }
  176|    454|    for (; i + 8 <= length; i += 8) {
  ------------------
  |  Branch (176:12): [True: 278, False: 176]
  ------------------
  177|    278|      uint64_t v;
  178|    278|      memcpy(&v, input + i, sizeof(v));
  179|    278|      answer += pop(v);
  180|    278|    }
  181|    176|  } // !consteval scope
  182|    669|  for (; i + 1 <= length; i += 1) {
  ------------------
  |  Branch (182:10): [True: 493, False: 176]
  ------------------
  183|    493|    answer += static_cast<uint8_t>(input[i]) >> 7;
  184|    493|  }
  185|    176|  return answer;
  186|    176|}
simdutf.cpp:_ZZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf823utf8_length_from_latin1IPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEEmS6_mENKUlmE_clEm:
  160|  2.50M|    auto pop = [](uint64_t v) {
  161|  2.50M|      return (size_t)(((v >> 7) & UINT64_C(0x0101010101010101)) *
  162|       |                          UINT64_C(0x0101010101010101) >>
  163|  2.50M|                      56);
  164|  2.50M|    };

_ZN7simdutf6scalar5utf1614swap_if_neededILNS_10endiannessE0EEEtt:
   27|  7.88M|template <endianness big_endian> constexpr uint16_t swap_if_needed(uint16_t c) {
   28|  7.88M|  return !match_system(big_endian) ? scalar::u16_swap_bytes(c) : c;
  ------------------
  |  Branch (28:10): [Folded, False: 7.88M]
  ------------------
   29|  7.88M|}

_ZN7simdutf6scalar5utf1614high_surrogateEDs:
  147|    739|simdutf_unused simdutf_really_inline constexpr bool high_surrogate(char16_t c) {
  148|    739|  return (0xd800 <= c && c <= 0xdbff);
  ------------------
  |  Branch (148:11): [True: 100, False: 639]
  |  Branch (148:26): [True: 48, False: 52]
  ------------------
  149|    739|}
_ZN7simdutf6scalar5utf1622utf8_length_from_utf16ILNS_10endiannessE0EEEmPKDsm:
   91|  1.00k|                                                  size_t len) {
   92|       |  // We are not BOM aware.
   93|  1.00k|  size_t counter{0};
   94|  7.84M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (94:22): [True: 7.84M, False: 1.00k]
  ------------------
   95|  7.84M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
   96|  7.84M|    counter++; // ASCII
   97|  7.84M|    counter += static_cast<size_t>(
   98|  7.84M|        word >
   99|  7.84M|        0x7F); // non-ASCII is at least 2 bytes, surrogates are 2*2 == 4 bytes
  100|  7.84M|    counter += static_cast<size_t>((word > 0x7FF && word <= 0xD7FF) ||
  ------------------
  |  Branch (100:37): [True: 1.06M, False: 6.78M]
  |  Branch (100:53): [True: 928k, False: 132k]
  ------------------
  101|  6.91M|                                   (word >= 0xE000)); // three-byte
  ------------------
  |  Branch (101:36): [True: 125k, False: 6.79M]
  ------------------
  102|  7.84M|  }
  103|  1.00k|  return counter;
  104|  1.00k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf819convert_with_errorsILNS_10endiannessE0ELb1EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT1_Esr7simdutf6detailE26index_assignable_from_charIT2_EEENS_11full_resultES8_mS9_m:
  101|    830|                                                    size_t utf8_len = 0) {
  102|    830|  if (check_output && utf8_len == 0) {
  ------------------
  |  Branch (102:7): [True: 830, Folded]
  |  Branch (102:23): [True: 63, False: 767]
  ------------------
  103|     63|    return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, 0, 0);
  104|     63|  }
  105|       |
  106|    767|  size_t pos = 0;
  107|    767|  auto start = utf8_output;
  108|    767|  auto end = utf8_output + utf8_len;
  109|       |
  110|  3.26k|  while (pos < len) {
  ------------------
  |  Branch (110:10): [True: 2.78k, False: 477]
  ------------------
  111|       |#if SIMDUTF_CPLUSPLUS23
  112|       |    if !consteval
  113|       |#endif
  114|  2.78k|    {
  115|       |      // try to convert the next block of 8 bytes
  116|  2.78k|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (116:11): [True: 2.16k, False: 624]
  ------------------
  117|       |                            // they are ascii
  118|  2.16k|        uint64_t v;
  119|  2.16k|        ::memcpy(&v, data + pos, sizeof(uint64_t));
  120|       |        if constexpr (!match_system(big_endian))
  121|       |          v = (v >> 8) | (v << (64 - 8));
  122|  2.16k|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (122:13): [True: 585, False: 1.57k]
  ------------------
  123|    585|          size_t final_pos = pos + 4;
  124|  2.82k|          while (pos < final_pos) {
  ------------------
  |  Branch (124:18): [True: 2.28k, False: 540]
  ------------------
  125|  2.28k|            if (check_output && size_t(end - utf8_output) < 1) {
  ------------------
  |  Branch (125:17): [True: 2.28k, Folded]
  |  Branch (125:33): [True: 45, False: 2.23k]
  ------------------
  126|     45|              return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  127|     45|                                 utf8_output - start);
  128|     45|            }
  129|  2.23k|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (129:30): [Folded, False: 2.23k]
  ------------------
  130|  2.23k|                                 ? char(u16_swap_bytes(data[pos]))
  131|  2.23k|                                 : char(data[pos]);
  132|  2.23k|            pos++;
  133|  2.23k|          }
  134|    540|          continue;
  135|    585|        }
  136|  2.16k|      }
  137|  2.78k|    }
  138|       |
  139|  2.20k|    uint16_t word =
  140|  2.20k|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (140:9): [Folded, False: 2.20k]
  ------------------
  141|  2.20k|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (141:9): [True: 569, False: 1.63k]
  ------------------
  142|       |      // will generate one UTF-8 bytes
  143|    569|      if (check_output && size_t(end - utf8_output) < 1) {
  ------------------
  |  Branch (143:11): [True: 569, Folded]
  |  Branch (143:27): [True: 13, False: 556]
  ------------------
  144|     13|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  145|     13|                           utf8_output - start);
  146|     13|      }
  147|    556|      *utf8_output++ = char(word);
  148|    556|      pos++;
  149|  1.63k|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (149:16): [True: 322, False: 1.31k]
  ------------------
  150|       |      // will generate two UTF-8 bytes
  151|       |      // we have 0b110XXXXX 0b10XXXXXX
  152|    322|      if (check_output && size_t(end - utf8_output) < 2) {
  ------------------
  |  Branch (152:11): [True: 322, Folded]
  |  Branch (152:27): [True: 16, False: 306]
  ------------------
  153|     16|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  154|     16|                           utf8_output - start);
  155|     16|      }
  156|    306|      *utf8_output++ = char((word >> 6) | 0b11000000);
  157|    306|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  158|    306|      pos++;
  159|       |
  160|  1.31k|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (160:16): [True: 1.05k, False: 252]
  ------------------
  161|       |      // will generate three UTF-8 bytes
  162|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
  163|  1.05k|      if (check_output && size_t(end - utf8_output) < 3) {
  ------------------
  |  Branch (163:11): [True: 1.05k, Folded]
  |  Branch (163:27): [True: 70, False: 988]
  ------------------
  164|     70|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  165|     70|                           utf8_output - start);
  166|     70|      }
  167|    988|      *utf8_output++ = char((word >> 12) | 0b11100000);
  168|    988|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  169|    988|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  170|    988|      pos++;
  171|    988|    } else {
  172|       |
  173|    252|      if (check_output && size_t(end - utf8_output) < 4) {
  ------------------
  |  Branch (173:11): [True: 252, Folded]
  |  Branch (173:27): [True: 26, False: 226]
  ------------------
  174|     26|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  175|     26|                           utf8_output - start);
  176|     26|      }
  177|       |      // must be a surrogate pair
  178|    226|      if (pos + 1 >= len) {
  ------------------
  |  Branch (178:11): [True: 53, False: 173]
  ------------------
  179|     53|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  180|     53|      }
  181|    173|      uint16_t diff = uint16_t(word - 0xD800);
  182|    173|      if (diff > 0x3FF) {
  ------------------
  |  Branch (182:11): [True: 24, False: 149]
  ------------------
  183|     24|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  184|     24|      }
  185|    149|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (185:28): [Folded, False: 149]
  ------------------
  186|    149|                               ? u16_swap_bytes(data[pos + 1])
  187|    149|                               : data[pos + 1];
  188|    149|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
  189|    149|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (189:11): [True: 43, False: 106]
  ------------------
  190|     43|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  191|     43|      }
  192|    106|      uint32_t value = (diff << 10) + diff2 + 0x10000;
  193|       |      // will generate four UTF-8 bytes
  194|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
  195|    106|      *utf8_output++ = char((value >> 18) | 0b11110000);
  196|    106|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  197|    106|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  198|    106|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
  199|    106|      pos += 2;
  200|    106|    }
  201|  2.20k|  }
  202|    477|  return full_result(error_code::SUCCESS, pos, utf8_output - start);
  203|    767|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf87convertILNS_10endiannessE0EPKDsPcQsr7simdutf6detailE18indexes_into_utf16IT0_EEEmS8_mT1_:
   17|  1.75k|                                   OutputPtr utf8_output) {
   18|  1.75k|  size_t pos = 0;
   19|  1.75k|  const auto start = utf8_output;
   20|  2.73M|  while (pos < len) {
  ------------------
  |  Branch (20:10): [True: 2.72M, False: 1.44k]
  ------------------
   21|       |#if SIMDUTF_CPLUSPLUS23
   22|       |    if !consteval
   23|       |#endif
   24|  2.72M|    {
   25|       |      // try to convert the next block of 8 bytes
   26|  2.72M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (26:11): [True: 2.72M, False: 3.11k]
  ------------------
   27|       |                            // they are ascii
   28|  2.72M|        uint64_t v;
   29|  2.72M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   30|       |        if constexpr (!match_system(big_endian)) {
   31|       |          v = (v >> 8) | (v << (64 - 8));
   32|       |        }
   33|  2.72M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (33:13): [True: 1.61M, False: 1.11M]
  ------------------
   34|  1.61M|          size_t final_pos = pos + 4;
   35|  8.06M|          while (pos < final_pos) {
  ------------------
  |  Branch (35:18): [True: 6.45M, False: 1.61M]
  ------------------
   36|  6.45M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (36:30): [Folded, False: 6.45M]
  ------------------
   37|  6.45M|                                 ? char(u16_swap_bytes(data[pos]))
   38|  6.45M|                                 : char(data[pos]);
   39|  6.45M|            pos++;
   40|  6.45M|          }
   41|  1.61M|          continue;
   42|  1.61M|        }
   43|  2.72M|      }
   44|  2.72M|    }
   45|  1.11M|    uint16_t word =
   46|  1.11M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (46:9): [Folded, False: 1.11M]
  ------------------
   47|  1.11M|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (47:9): [True: 20.7k, False: 1.09M]
  ------------------
   48|       |      // will generate one UTF-8 bytes
   49|  20.7k|      *utf8_output++ = char(word);
   50|  20.7k|      pos++;
   51|  1.09M|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (51:16): [True: 49.2k, False: 1.04M]
  ------------------
   52|       |      // will generate two UTF-8 bytes
   53|       |      // we have 0b110XXXXX 0b10XXXXXX
   54|  49.2k|      *utf8_output++ = char((word >> 6) | 0b11000000);
   55|  49.2k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   56|  49.2k|      pos++;
   57|  1.04M|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (57:16): [True: 1.04M, False: 4.08k]
  ------------------
   58|       |      // will generate three UTF-8 bytes
   59|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
   60|  1.04M|      *utf8_output++ = char((word >> 12) | 0b11100000);
   61|  1.04M|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   62|  1.04M|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   63|  1.04M|      pos++;
   64|  1.04M|    } else {
   65|       |      // must be a surrogate pair
   66|  4.08k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (66:11): [True: 100, False: 3.98k]
  ------------------
   67|    100|        return 0;
   68|    100|      }
   69|  3.98k|      uint16_t diff = uint16_t(word - 0xD800);
   70|  3.98k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (70:11): [True: 81, False: 3.89k]
  ------------------
   71|     81|        return 0;
   72|     81|      }
   73|  3.89k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (73:28): [Folded, False: 3.89k]
  ------------------
   74|  3.89k|                               ? u16_swap_bytes(data[pos + 1])
   75|  3.89k|                               : data[pos + 1];
   76|  3.89k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   77|  3.89k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (77:11): [True: 128, False: 3.77k]
  ------------------
   78|    128|        return 0;
   79|    128|      }
   80|  3.77k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   81|       |      // will generate four UTF-8 bytes
   82|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
   83|  3.77k|      *utf8_output++ = char((value >> 18) | 0b11110000);
   84|  3.77k|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
   85|  3.77k|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
   86|  3.77k|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
   87|  3.77k|      pos += 2;
   88|  3.77k|    }
   89|  1.11M|  }
   90|  1.44k|  return utf8_output - start;
   91|  1.75k|}

_ZNK7simdutf8fallback14implementation22convert_latin1_to_utf8EPKcmPc:
  128|    501|    const char *buf, size_t len, char *utf8_output) const noexcept {
  129|    501|  return scalar::latin1_to_utf8::convert(buf, len, utf8_output);
  130|    501|}
_ZNK7simdutf8fallback14implementation23convert_utf16le_to_utf8EPKDsmPc:
  268|    599|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  269|    599|  return scalar::utf16_to_utf8::convert<endianness::LITTLE>(buf, len,
  270|    599|                                                            utf8_output);
  271|    599|}
_ZNK7simdutf8fallback14implementation23utf8_length_from_latin1EPKcm:
  445|    176|    const char *input, size_t length) const noexcept {
  446|    176|  return scalar::latin1_to_utf8::utf8_length_from_latin1(input, length);
  447|    176|}
_ZNK7simdutf8fallback14implementation24utf8_length_from_utf16leEPKDsm:
  452|    227|    const char16_t *input, size_t length) const noexcept {
  453|    227|  return scalar::utf16::utf8_length_from_utf16<endianness::LITTLE>(input,
  454|    227|                                                                   length);
  455|    227|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1631utf8_length_from_utf16_bytemaskILNS_10endiannessE0EEEmPKDsm:
   10|    383|                                                             size_t size) {
   11|    383|  size_t pos = 0;
   12|       |
   13|    383|  using vector_u16 = simd16<uint16_t>;
   14|    383|  constexpr size_t N = vector_u16::ELEMENTS;
   15|       |
   16|    383|  const auto one = vector_u16::splat(1);
   17|       |
   18|    383|  auto v_count = vector_u16::zero();
   19|       |
   20|       |  // each char16 yields at least one byte
   21|    383|  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|    383|  constexpr size_t max_iterations = 65535 / 2;
   26|    383|  size_t iteration = max_iterations;
   27|       |
   28|   353k|  for (; pos < size / N * N; pos += N) {
  ------------------
  |  Branch (28:10): [True: 352k, False: 383]
  ------------------
   29|   352k|    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|   352k|    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|   352k|    const auto c0 = min(input & uint16_t(0xff80), one);
   39|       |
   40|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
   41|   352k|    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|   352k|    v_count += c0;
   68|   352k|    v_count += c1;
   69|   352k|    v_count += vector_u16(is_surrogate);
   70|       |
   71|   352k|    iteration -= 1;
   72|   352k|    if (iteration == 0) {
  ------------------
  |  Branch (72:9): [True: 5, False: 352k]
  ------------------
   73|      5|      count += v_count.sum();
   74|      5|      v_count = vector_u16::zero();
   75|      5|      iteration = max_iterations;
   76|      5|    }
   77|   352k|  }
   78|       |
   79|    383|  if (iteration > 0) {
  ------------------
  |  Branch (79:7): [True: 383, False: 0]
  ------------------
   80|    383|    count += v_count.sum();
   81|    383|  }
   82|       |
   83|    383|  return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos,
   84|    383|                                                                   size - pos);
   85|    383|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1631utf8_length_from_utf16_bytemaskILNS_10endiannessE0EEEmPKDsm:
   10|    395|                                                             size_t size) {
   11|    395|  size_t pos = 0;
   12|       |
   13|    395|  using vector_u16 = simd16<uint16_t>;
   14|    395|  constexpr size_t N = vector_u16::ELEMENTS;
   15|       |
   16|    395|  const auto one = vector_u16::splat(1);
   17|       |
   18|    395|  auto v_count = vector_u16::zero();
   19|       |
   20|       |  // each char16 yields at least one byte
   21|    395|  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|    395|  constexpr size_t max_iterations = 65535 / 2;
   26|    395|  size_t iteration = max_iterations;
   27|       |
   28|   959k|  for (; pos < size / N * N; pos += N) {
  ------------------
  |  Branch (28:10): [True: 959k, False: 395]
  ------------------
   29|   959k|    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|   959k|    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|   959k|    const auto c0 = min(input & uint16_t(0xff80), one);
   39|       |
   40|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
   41|   959k|    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|   959k|    v_count += c0;
   68|   959k|    v_count += c1;
   69|   959k|    v_count += vector_u16(is_surrogate);
   70|       |
   71|   959k|    iteration -= 1;
   72|   959k|    if (iteration == 0) {
  ------------------
  |  Branch (72:9): [True: 17, False: 959k]
  ------------------
   73|     17|      count += v_count.sum();
   74|     17|      v_count = vector_u16::zero();
   75|     17|      iteration = max_iterations;
   76|     17|    }
   77|   959k|  }
   78|       |
   79|    395|  if (iteration > 0) {
  ------------------
  |  Branch (79:7): [True: 395, False: 0]
  ------------------
   80|    395|    count += v_count.sum();
   81|    395|  }
   82|       |
   83|    395|  return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos,
   84|    395|                                                                   size - pos);
   85|    395|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_127avx2_convert_latin1_to_utf8EPKcmPc:
    3|    753|                            char *utf8_output) {
    4|    753|  const char *end = latin1_input + len;
    5|    753|  const __m256i v_0000 = _mm256_setzero_si256();
    6|    753|  const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
    7|    753|  const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
    8|    753|  const size_t safety_margin = 12;
    9|       |
   10|  1.48M|  while (end - latin1_input >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (10:10): [True: 1.48M, False: 753]
  ------------------
   11|  1.48M|    __m128i in8 = _mm_loadu_si128((__m128i *)latin1_input);
   12|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
   13|  1.48M|    const __m128i v_80 = _mm_set1_epi8((char)0x80);
   14|  1.48M|    if (_mm_testz_si128(in8, v_80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (14:9): [True: 1.19M, False: 289k]
  ------------------
   15|       |      // 1. store (16 bytes)
   16|  1.19M|      _mm_storeu_si128((__m128i *)utf8_output, in8);
   17|       |      // 2. adjust pointers
   18|  1.19M|      latin1_input += 16;
   19|  1.19M|      utf8_output += 16;
   20|  1.19M|      continue; // we are done for this round!
   21|  1.19M|    }
   22|       |    // We proceed only with the first 16 bytes.
   23|   289k|    const __m256i in = _mm256_cvtepu8_epi16((in8));
   24|       |
   25|       |    // 1. prepare 2-byte values
   26|       |    // input 16-bit word : [0000|0000|aabb|bbbb] x 8
   27|       |    // expected output   : [1100|00aa|10bb|bbbb] x 8
   28|   289k|    const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
   29|   289k|    const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
   30|       |
   31|       |    // t0 = [0000|00aa|bbbb|bb00]
   32|   289k|    const __m256i t0 = _mm256_slli_epi16(in, 2);
   33|       |    // t1 = [0000|00aa|0000|0000]
   34|   289k|    const __m256i t1 = _mm256_and_si256(t0, v_1f00);
   35|       |    // t2 = [0000|0000|00bb|bbbb]
   36|   289k|    const __m256i t2 = _mm256_and_si256(in, v_003f);
   37|       |    // t3 = [000a|aaaa|00bb|bbbb]
   38|   289k|    const __m256i t3 = _mm256_or_si256(t1, t2);
   39|       |    // t4 = [1100|00aa|10bb|bbbb]
   40|   289k|    const __m256i t4 = _mm256_or_si256(t3, v_c080);
   41|       |
   42|       |    // 2. merge ASCII and 2-byte codewords
   43|       |
   44|       |    // no bits set above 7th bit
   45|   289k|    const __m256i one_byte_bytemask =
   46|   289k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
   47|   289k|    const uint32_t one_byte_bitmask =
   48|   289k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
   49|       |
   50|   289k|    const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in, one_byte_bytemask);
   51|       |
   52|       |    // 3. prepare bitmask for 8-bit lookup
   53|   289k|    const uint32_t M0 = one_byte_bitmask & 0x55555555;
   54|   289k|    const uint32_t M1 = M0 >> 7;
   55|   289k|    const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
   56|       |    // 4. pack the bytes
   57|       |
   58|   289k|    const uint8_t *row =
   59|   289k|        &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
   60|   289k|    const uint8_t *row_2 =
   61|   289k|        &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >> 16)]
   62|   289k|                                                            [0];
   63|       |
   64|   289k|    const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
   65|   289k|    const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
   66|       |
   67|   289k|    const __m256i utf8_packed = _mm256_shuffle_epi8(
   68|   289k|        utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|   289k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|   289k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
   69|       |    // 5. store bytes
   70|   289k|    _mm_storeu_si128((__m128i *)utf8_output,
   71|   289k|                     _mm256_castsi256_si128(utf8_packed));
   72|   289k|    utf8_output += row[0];
   73|   289k|    _mm_storeu_si128((__m128i *)utf8_output,
   74|   289k|                     _mm256_extractf128_si256(utf8_packed, 1));
   75|   289k|    utf8_output += row_2[0];
   76|       |
   77|       |    // 6. adjust pointers
   78|   289k|    latin1_input += 16;
   79|   289k|    continue;
   80|       |
   81|  1.48M|  } // while
   82|    753|  return std::make_pair(latin1_input, utf8_output);
   83|    753|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_126avx2_convert_utf16_to_utf8ILNS_10endiannessE0EEENSt3__14pairIPKDsPcEES7_mS8_:
   56|    677|avx2_convert_utf16_to_utf8(const char16_t *buf, size_t len, char *utf8_output) {
   57|    677|  const char16_t *end = buf + len;
   58|    677|  const __m256i v_0000 = _mm256_setzero_si256();
   59|    677|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
   60|    677|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
   61|    677|  const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
   62|    677|  const size_t safety_margin =
   63|    677|      12; // to avoid overruns, see issue
   64|       |          // https://github.com/simdutf/simdutf/issues/92
   65|       |
   66|   118k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (66:10): [True: 118k, False: 532]
  ------------------
   67|   118k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
   68|   118k|    if (big_endian) {
  ------------------
  |  Branch (68:9): [Folded, False: 118k]
  ------------------
   69|      0|      const __m256i swap = _mm256_setr_epi8(
   70|      0|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   71|      0|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   72|      0|      in = _mm256_shuffle_epi8(in, swap);
   73|      0|    }
   74|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
   75|   118k|    const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
   76|   118k|    if (_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (76:9): [True: 86.0k, False: 32.1k]
  ------------------
   77|       |      // 1. pack the bytes
   78|  86.0k|      const __m128i utf8_packed = _mm_packus_epi16(
   79|  86.0k|          _mm256_castsi256_si128(in), _mm256_extractf128_si256(in, 1));
   80|       |      // 2. store (16 bytes)
   81|  86.0k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   82|       |      // 3. adjust pointers
   83|  86.0k|      buf += 16;
   84|  86.0k|      utf8_output += 16;
   85|  86.0k|      continue; // we are done for this round!
   86|  86.0k|    }
   87|       |    // no bits set above 7th bit
   88|  32.1k|    const __m256i one_byte_bytemask =
   89|  32.1k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
   90|  32.1k|    const uint32_t one_byte_bitmask =
   91|  32.1k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
   92|       |
   93|       |    // no bits set above 11th bit
   94|  32.1k|    const __m256i one_or_two_bytes_bytemask =
   95|  32.1k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000);
   96|  32.1k|    const uint32_t one_or_two_bytes_bitmask =
   97|  32.1k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
   98|  32.1k|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (98:9): [True: 1.08k, False: 31.0k]
  ------------------
   99|       |
  100|       |      // 1. prepare 2-byte values
  101|       |      // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
  102|       |      // expected output   : [110a|aaaa|10bb|bbbb] x 8
  103|  1.08k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
  104|  1.08k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
  105|       |
  106|       |      // t0 = [000a|aaaa|bbbb|bb00]
  107|  1.08k|      const __m256i t0 = _mm256_slli_epi16(in, 2);
  108|       |      // t1 = [000a|aaaa|0000|0000]
  109|  1.08k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
  110|       |      // t2 = [0000|0000|00bb|bbbb]
  111|  1.08k|      const __m256i t2 = _mm256_and_si256(in, v_003f);
  112|       |      // t3 = [000a|aaaa|00bb|bbbb]
  113|  1.08k|      const __m256i t3 = _mm256_or_si256(t1, t2);
  114|       |      // t4 = [110a|aaaa|10bb|bbbb]
  115|  1.08k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
  116|       |
  117|       |      // 2. merge ASCII and 2-byte codewords
  118|  1.08k|      const __m256i utf8_unpacked =
  119|  1.08k|          _mm256_blendv_epi8(t4, in, one_byte_bytemask);
  120|       |
  121|       |      // 3. prepare bitmask for 8-bit lookup
  122|  1.08k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
  123|  1.08k|      const uint32_t M1 = M0 >> 7;
  124|  1.08k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
  125|       |      // 4. pack the bytes
  126|       |
  127|  1.08k|      const uint8_t *row =
  128|  1.08k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
  129|  1.08k|      const uint8_t *row_2 =
  130|  1.08k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
  131|  1.08k|                                                                       16)][0];
  132|       |
  133|  1.08k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  134|  1.08k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
  135|       |
  136|  1.08k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
  137|  1.08k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|  1.08k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|  1.08k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  138|       |      // 5. store bytes
  139|  1.08k|      _mm_storeu_si128((__m128i *)utf8_output,
  140|  1.08k|                       _mm256_castsi256_si128(utf8_packed));
  141|  1.08k|      utf8_output += row[0];
  142|  1.08k|      _mm_storeu_si128((__m128i *)utf8_output,
  143|  1.08k|                       _mm256_extractf128_si256(utf8_packed, 1));
  144|  1.08k|      utf8_output += row_2[0];
  145|       |
  146|       |      // 6. adjust pointers
  147|  1.08k|      buf += 16;
  148|  1.08k|      continue;
  149|  1.08k|    }
  150|       |    // 1. Check if there are any surrogate word in the input chunk.
  151|       |    //    We have also deal with situation when there is a surrogate word
  152|       |    //    at the end of a chunk.
  153|  31.0k|    const __m256i surrogates_bytemask =
  154|  31.0k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800);
  155|       |
  156|       |    // bitmask = 0x0000 if there are no surrogates
  157|       |    //         = 0xc000 if the last word is a surrogate
  158|  31.0k|    const uint32_t surrogates_bitmask =
  159|  31.0k|        static_cast<uint32_t>(_mm256_movemask_epi8(surrogates_bytemask));
  160|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
  161|       |    // However, it is likely an uncommon occurrence.
  162|  31.0k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (162:9): [True: 29.7k, False: 1.23k]
  ------------------
  163|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  164|  29.7k|      const __m256i dup_even = _mm256_setr_epi16(
  165|  29.7k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  166|  29.7k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
  167|       |
  168|       |      /* In this branch we handle three cases:
  169|       |         1. [0000|0000|0ccc|cccc] => [0ccc|cccc]                           -
  170|       |        single UFT-8 byte
  171|       |         2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc]              - two
  172|       |        UTF-8 bytes
  173|       |         3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] -
  174|       |        three UTF-8 bytes
  175|       |
  176|       |        We expand the input word (16-bit) into two code units (32-bit), thus
  177|       |        we have room for four bytes. However, we need five distinct bit
  178|       |        layouts. Note that the last byte in cases #2 and #3 is the same.
  179|       |
  180|       |        We precompute byte 1 for case #1 and the common byte for cases #2 & #3
  181|       |        in register t2.
  182|       |
  183|       |        We precompute byte 1 for case #3 and -- **conditionally** -- precompute
  184|       |        either byte 1 for case #2 or byte 2 for case #3. Note that they
  185|       |        differ by exactly one bit.
  186|       |
  187|       |        Finally from these two code units we build proper UTF-8 sequence, taking
  188|       |        into account the case (i.e, the number of bytes to write).
  189|       |      */
  190|       |      /**
  191|       |       * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
  192|       |       * t2 => [0ccc|cccc] [10cc|cccc]
  193|       |       * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
  194|       |       */
  195|  29.7k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  196|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  197|  29.7k|      const __m256i t0 = _mm256_shuffle_epi8(in, dup_even);
  198|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  199|  29.7k|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  195|  29.7k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  200|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  201|  29.7k|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  195|  29.7k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  202|       |
  203|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  204|  29.7k|      const __m256i s0 = _mm256_srli_epi16(in, 4);
  205|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  206|  29.7k|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  195|  29.7k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  207|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  208|  29.7k|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  195|  29.7k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  209|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  210|  29.7k|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  195|  29.7k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  211|  29.7k|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  212|  29.7k|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  195|  29.7k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  213|  29.7k|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  214|  29.7k|#undef simdutf_vec
  215|       |
  216|       |      // 4. expand code units 16-bit => 32-bit
  217|  29.7k|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  218|  29.7k|      const __m256i out1 = _mm256_unpackhi_epi16(t2, s4);
  219|       |
  220|       |      // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
  221|  29.7k|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  222|  29.7k|                            (one_or_two_bytes_bitmask & 0xaaaaaaaa);
  223|       |      // Due to the wider registers, the following path is less likely to be
  224|       |      // useful.
  225|       |      /*if(mask == 0) {
  226|       |        // We only have three-byte code units. Use fast path.
  227|       |        const __m256i shuffle =
  228|       |      _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1,
  229|       |      2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); const __m256i utf8_0 =
  230|       |      _mm256_shuffle_epi8(out0, shuffle); const __m256i utf8_1 =
  231|       |      _mm256_shuffle_epi8(out1, shuffle);
  232|       |        _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0));
  233|       |        utf8_output += 12;
  234|       |        _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1));
  235|       |        utf8_output += 12;
  236|       |        _mm_storeu_si128((__m128i*)utf8_output,
  237|       |      _mm256_extractf128_si256(utf8_0,1)); utf8_output += 12;
  238|       |        _mm_storeu_si128((__m128i*)utf8_output,
  239|       |      _mm256_extractf128_si256(utf8_1,1)); utf8_output += 12; buf += 16;
  240|       |        continue;
  241|       |      }*/
  242|  29.7k|      const uint8_t mask0 = uint8_t(mask);
  243|  29.7k|      const uint8_t *row0 =
  244|  29.7k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  245|  29.7k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  246|  29.7k|      const __m128i utf8_0 =
  247|  29.7k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  248|       |
  249|  29.7k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  250|  29.7k|      const uint8_t *row1 =
  251|  29.7k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  252|  29.7k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  253|  29.7k|      const __m128i utf8_1 =
  254|  29.7k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  255|       |
  256|  29.7k|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  257|  29.7k|      const uint8_t *row2 =
  258|  29.7k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  259|  29.7k|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  260|  29.7k|      const __m128i utf8_2 =
  261|  29.7k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  262|       |
  263|  29.7k|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  264|  29.7k|      const uint8_t *row3 =
  265|  29.7k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  266|  29.7k|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  267|  29.7k|      const __m128i utf8_3 =
  268|  29.7k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  269|       |
  270|  29.7k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  271|  29.7k|      utf8_output += row0[0];
  272|  29.7k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  273|  29.7k|      utf8_output += row1[0];
  274|  29.7k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  275|  29.7k|      utf8_output += row2[0];
  276|  29.7k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  277|  29.7k|      utf8_output += row3[0];
  278|  29.7k|      buf += 16;
  279|       |      // surrogate pair(s) in a register
  280|  29.7k|    } else {
  281|       |      // Let us do a scalar fallback.
  282|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
  283|       |      // in the presence of surrogate pairs may require non-trivial tables.
  284|  1.23k|      size_t forward = 15;
  285|  1.23k|      size_t k = 0;
  286|  1.23k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (286:11): [True: 0, False: 1.23k]
  ------------------
  287|      0|        forward = size_t(end - buf - 1);
  288|      0|      }
  289|  16.1k|      for (; k < forward; k++) {
  ------------------
  |  Branch (289:14): [True: 15.0k, False: 1.08k]
  ------------------
  290|  15.0k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  291|  15.0k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (291:13): [True: 7.17k, False: 7.90k]
  ------------------
  292|  7.17k|          *utf8_output++ = char(word);
  293|  7.90k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (293:20): [True: 2.12k, False: 5.77k]
  ------------------
  294|  2.12k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  295|  2.12k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  296|  5.77k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (296:20): [True: 3.27k, False: 2.50k]
  ------------------
  297|  3.27k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  298|  3.27k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  299|  3.27k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  300|  3.27k|        } else {
  301|       |          // must be a surrogate pair
  302|  2.50k|          uint16_t diff = uint16_t(word - 0xD800);
  303|  2.50k|          uint16_t next_word =
  304|  2.50k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  305|  2.50k|          k++;
  306|  2.50k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  307|  2.50k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (307:15): [True: 145, False: 2.36k]
  ------------------
  308|    145|            return std::make_pair(nullptr, utf8_output);
  309|    145|          }
  310|  2.36k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  311|  2.36k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  312|  2.36k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  313|  2.36k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  314|  2.36k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  315|  2.36k|        }
  316|  15.0k|      }
  317|  1.08k|      buf += k;
  318|  1.08k|    }
  319|  31.0k|  } // while
  320|    532|  return std::make_pair(buf, utf8_output);
  321|    677|}

_ZNK7simdutf7haswell14implementation22convert_latin1_to_utf8EPKcmPc:
  412|    753|    const char *buf, size_t len, char *utf8_output) const noexcept {
  413|    753|  std::pair<const char *, char *> ret =
  414|    753|      avx2_convert_latin1_to_utf8(buf, len, utf8_output);
  415|    753|  size_t converted_chars = ret.second - utf8_output;
  416|       |
  417|    753|  if (ret.first != buf + len) {
  ------------------
  |  Branch (417:7): [True: 738, False: 15]
  ------------------
  418|    738|    const size_t scalar_converted_chars = scalar::latin1_to_utf8::convert(
  419|    738|        ret.first, len - (ret.first - buf), ret.second);
  420|    738|    converted_chars += scalar_converted_chars;
  421|    738|  }
  422|       |
  423|    753|  return converted_chars;
  424|    753|}
_ZNK7simdutf7haswell14implementation23convert_utf16le_to_utf8EPKDsmPc:
  679|    677|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  680|    677|  std::pair<const char16_t *, char *> ret =
  681|    677|      haswell::avx2_convert_utf16_to_utf8<endianness::LITTLE>(buf, len,
  682|    677|                                                              utf8_output);
  683|    677|  if (ret.first == nullptr) {
  ------------------
  |  Branch (683:7): [True: 145, False: 532]
  ------------------
  684|    145|    return 0;
  685|    145|  }
  686|    532|  size_t saved_bytes = ret.second - utf8_output;
  687|    532|  if (ret.first != buf + len) {
  ------------------
  |  Branch (687:7): [True: 521, False: 11]
  ------------------
  688|    521|    const size_t scalar_saved_bytes =
  689|    521|        scalar::utf16_to_utf8::convert<endianness::LITTLE>(
  690|    521|            ret.first, len - (ret.first - buf), ret.second);
  691|    521|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (691:9): [True: 101, False: 420]
  ------------------
  692|    101|      return 0;
  693|    101|    }
  694|    420|    saved_bytes += scalar_saved_bytes;
  695|    420|  }
  696|    431|  return saved_bytes;
  697|    532|}
_ZNK7simdutf7haswell14implementation24utf8_length_from_utf16leEPKDsm:
 1108|    383|    const char16_t *input, size_t length) const noexcept {
 1109|    383|  return utf16::utf8_length_from_utf16_bytemask<endianness::LITTLE>(input,
 1110|    383|                                                                    length);
 1111|    383|}
_ZNK7simdutf7haswell14implementation23utf8_length_from_latin1EPKcm:
 1174|    272|    const char *input, size_t len) const noexcept {
 1175|    272|  const uint8_t *data = reinterpret_cast<const uint8_t *>(input);
 1176|    272|  size_t answer = len / sizeof(__m256i) * sizeof(__m256i);
 1177|    272|  size_t i = 0;
 1178|    272|  if (answer >= 2048) { // long strings optimization
  ------------------
  |  Branch (1178:7): [True: 117, False: 155]
  ------------------
 1179|    117|    __m256i four_64bits = _mm256_setzero_si256();
 1180|  2.91k|    while (i + sizeof(__m256i) <= len) {
  ------------------
  |  Branch (1180:12): [True: 2.80k, False: 117]
  ------------------
 1181|  2.80k|      __m256i runner = _mm256_setzero_si256();
 1182|       |      // We can do up to 255 loops without overflow.
 1183|  2.80k|      size_t iterations = (len - i) / sizeof(__m256i);
 1184|  2.80k|      if (iterations > 255) {
  ------------------
  |  Branch (1184:11): [True: 2.68k, False: 117]
  ------------------
 1185|  2.68k|        iterations = 255;
 1186|  2.68k|      }
 1187|  2.80k|      size_t max_i = i + iterations * sizeof(__m256i) - sizeof(__m256i);
 1188|   174k|      for (; i + 4 * sizeof(__m256i) <= max_i; i += 4 * sizeof(__m256i)) {
  ------------------
  |  Branch (1188:14): [True: 171k, False: 2.80k]
  ------------------
 1189|   171k|        __m256i input1 = _mm256_loadu_si256((const __m256i *)(data + i));
 1190|   171k|        __m256i input2 =
 1191|   171k|            _mm256_loadu_si256((const __m256i *)(data + i + sizeof(__m256i)));
 1192|   171k|        __m256i input3 = _mm256_loadu_si256(
 1193|   171k|            (const __m256i *)(data + i + 2 * sizeof(__m256i)));
 1194|   171k|        __m256i input4 = _mm256_loadu_si256(
 1195|   171k|            (const __m256i *)(data + i + 3 * sizeof(__m256i)));
 1196|   171k|        __m256i input12 =
 1197|   171k|            _mm256_add_epi8(_mm256_cmpgt_epi8(_mm256_setzero_si256(), input1),
 1198|   171k|                            _mm256_cmpgt_epi8(_mm256_setzero_si256(), input2));
 1199|   171k|        __m256i input23 =
 1200|   171k|            _mm256_add_epi8(_mm256_cmpgt_epi8(_mm256_setzero_si256(), input3),
 1201|   171k|                            _mm256_cmpgt_epi8(_mm256_setzero_si256(), input4));
 1202|   171k|        __m256i input1234 = _mm256_add_epi8(input12, input23);
 1203|   171k|        runner = _mm256_sub_epi8(runner, input1234);
 1204|   171k|      }
 1205|  11.1k|      for (; i <= max_i; i += sizeof(__m256i)) {
  ------------------
  |  Branch (1205:14): [True: 8.35k, False: 2.80k]
  ------------------
 1206|  8.35k|        __m256i input_256_chunk =
 1207|  8.35k|            _mm256_loadu_si256((const __m256i *)(data + i));
 1208|  8.35k|        runner = _mm256_sub_epi8(
 1209|  8.35k|            runner, _mm256_cmpgt_epi8(_mm256_setzero_si256(), input_256_chunk));
 1210|  8.35k|      }
 1211|  2.80k|      four_64bits = _mm256_add_epi64(
 1212|  2.80k|          four_64bits, _mm256_sad_epu8(runner, _mm256_setzero_si256()));
 1213|  2.80k|    }
 1214|    117|    answer += _mm256_extract_epi64(four_64bits, 0) +
 1215|    117|              _mm256_extract_epi64(four_64bits, 1) +
 1216|    117|              _mm256_extract_epi64(four_64bits, 2) +
 1217|    117|              _mm256_extract_epi64(four_64bits, 3);
 1218|    155|  } else if (answer > 0) {
  ------------------
  |  Branch (1218:14): [True: 74, False: 81]
  ------------------
 1219|  1.33k|    for (; i + sizeof(__m256i) <= len; i += sizeof(__m256i)) {
  ------------------
  |  Branch (1219:12): [True: 1.25k, False: 74]
  ------------------
 1220|  1.25k|      __m256i latin = _mm256_loadu_si256((const __m256i *)(data + i));
 1221|  1.25k|      uint32_t non_ascii = _mm256_movemask_epi8(latin);
 1222|  1.25k|      answer += count_ones(non_ascii);
 1223|  1.25k|    }
 1224|     74|  }
 1225|    272|  return answer + scalar::latin1::utf8_length_from_latin1(
 1226|    272|                      reinterpret_cast<const char *>(data + i), len - i);
 1227|    272|}

_ZNK7simdutf14implementation27supported_by_runtime_systemEv:
   64|      4|bool implementation::supported_by_runtime_system() const {
   65|      4|  uint32_t required_instruction_sets = this->required_instruction_sets();
   66|      4|  uint32_t supported_instruction_sets =
   67|      4|      internal::detect_supported_architectures();
   68|      4|  return ((supported_instruction_sets & required_instruction_sets) ==
   69|      4|          required_instruction_sets);
   70|      4|}
_ZNK7simdutf8internal29available_implementation_list5beginEv:
 1503|      1|available_implementation_list::begin() const noexcept {
 1504|      1|  return internal::get_available_implementation_pointers().begin();
 1505|      1|}
_ZNK7simdutf8internal29available_implementation_list3endEv:
 1507|      1|available_implementation_list::end() const noexcept {
 1508|      1|  return internal::get_available_implementation_pointers().end();
 1509|      1|}
_ZN7simdutf29get_available_implementationsEv:
 1558|      1|get_available_implementations() {
 1559|      1|#if !SIMDUTF_USE_STATIC_INITIALIZATION
 1560|      1|  static const internal::available_implementation_list
 1561|      1|      available_implementations_instance{};
 1562|      1|#endif
 1563|      1|  return available_implementations_instance;
 1564|      1|}
_ZN7simdutf25get_active_implementationEv:
 1586|  7.63k|get_active_implementation() {
 1587|  7.63k|#if !SIMDUTF_USE_STATIC_INITIALIZATION
 1588|  7.63k|  #if !SIMDUTF_SINGLE_IMPLEMENTATION
 1589|  7.63k|  static const internal::detect_best_supported_implementation_on_first_use
 1590|  7.63k|      detect_best_supported_implementation_on_first_use_singleton;
 1591|  7.63k|  #endif
 1592|  7.63k|  static internal::atomic_ptr<const implementation>
 1593|  7.63k|      active_implementation_instance{
 1594|       |  #if SIMDUTF_SINGLE_IMPLEMENTATION
 1595|       |          internal::get_single_implementation()
 1596|       |  #else
 1597|  7.63k|          &detect_best_supported_implementation_on_first_use_singleton
 1598|  7.63k|  #endif
 1599|  7.63k|      };
 1600|  7.63k|#endif
 1601|  7.63k|  return active_implementation_instance;
 1602|  7.63k|}
_ZN7simdutf22convert_latin1_to_utf8EPKcmPc:
 1668|  2.13k|                                                  char *utf8_output) noexcept {
 1669|  2.13k|  return get_default_implementation()->convert_latin1_to_utf8(buf, len,
 1670|  2.13k|                                                              utf8_output);
 1671|  2.13k|}
_ZN7simdutf21convert_utf16_to_utf8EPKDsmPc:
 1954|  2.03k|                                                 char *utf8_buffer) noexcept {
 1955|       |  #if SIMDUTF_IS_BIG_ENDIAN
 1956|       |  return convert_utf16be_to_utf8(buf, len, utf8_buffer);
 1957|       |  #else
 1958|  2.03k|  return convert_utf16le_to_utf8(buf, len, utf8_buffer);
 1959|  2.03k|  #endif
 1960|  2.03k|}
_ZN7simdutf26convert_utf16_to_utf8_safeEPKDsmPcm:
 1964|  1.00k|                           size_t utf8_len) noexcept {
 1965|  1.00k|  const auto start{utf8_output};
 1966|       |  // We might be able to go faster by first scanning the input buffer to
 1967|       |  // determine how many char16_t characters we can read without exceeding the
 1968|       |  // utf8_len. This is a one-pass algorithm that has the benefit of not
 1969|       |  // requiring a first pass to determine the length.
 1970|  1.85k|  while (true) {
  ------------------
  |  Branch (1970:10): [True: 1.85k, Folded]
  ------------------
 1971|       |    // The worst case for convert_utf16_to_utf8 is when you go from 1 char16_t
 1972|       |    // to 3 characters of UTF-8. So we can read at most utf8_len / 3 char16_t
 1973|       |    // characters.
 1974|  1.85k|    auto read_len = detail::min(len, utf8_len / 3);
 1975|  1.85k|    if (read_len <= 16) {
  ------------------
  |  Branch (1975:9): [True: 830, False: 1.02k]
  ------------------
 1976|    830|      break;
 1977|    830|    }
 1978|  1.02k|    if (read_len < len) {
  ------------------
  |  Branch (1978:9): [True: 739, False: 287]
  ------------------
 1979|       |      //  If we have a high surrogate at the end of the buffer, we need to
 1980|       |      //  either read one more char16_t or backtrack.
 1981|    739|      if (scalar::utf16::high_surrogate(buf[read_len - 1])) {
  ------------------
  |  Branch (1981:11): [True: 48, False: 691]
  ------------------
 1982|     48|        read_len--;
 1983|     48|      }
 1984|    739|    }
 1985|  1.02k|    if (read_len == 0) {
  ------------------
  |  Branch (1985:9): [True: 0, False: 1.02k]
  ------------------
 1986|       |      // If we cannot read anything, we are done.
 1987|      0|      break;
 1988|      0|    }
 1989|  1.02k|    const auto write_len =
 1990|  1.02k|        simdutf::convert_utf16_to_utf8(buf, read_len, utf8_output);
 1991|  1.02k|    if (write_len == 0) {
  ------------------
  |  Branch (1991:9): [True: 175, False: 851]
  ------------------
 1992|       |      // There was an error in the conversion, we cannot continue.
 1993|    175|      return 0; // indicating failure
 1994|    175|    }
 1995|       |
 1996|    851|    utf8_output += write_len;
 1997|    851|    utf8_len -= write_len;
 1998|    851|    buf += read_len;
 1999|    851|    len -= read_len;
 2000|    851|  }
 2001|       |  #if SIMDUTF_IS_BIG_ENDIAN
 2002|       |  full_result r =
 2003|       |      scalar::utf16_to_utf8::convert_with_errors<endianness::BIG, true>(
 2004|       |          buf, len, utf8_output, utf8_len);
 2005|       |  #else
 2006|    830|  full_result r =
 2007|    830|      scalar::utf16_to_utf8::convert_with_errors<endianness::LITTLE, true>(
 2008|    830|          buf, len, utf8_output, utf8_len);
 2009|    830|  #endif
 2010|    830|  if (r.error != error_code::SUCCESS &&
  ------------------
  |  Branch (2010:7): [True: 353, False: 477]
  ------------------
 2011|    353|      r.error != error_code::OUTPUT_BUFFER_TOO_SMALL) {
  ------------------
  |  Branch (2011:7): [True: 120, False: 233]
  ------------------
 2012|       |    // If there was an error, we return 0 to indicate failure.
 2013|    120|    return 0; // indicating failure
 2014|    120|  }
 2015|    710|  return r.output_count + (utf8_output - start);
 2016|    830|}
_ZN7simdutf23convert_utf16le_to_utf8EPKDsmPc:
 2074|  2.03k|                                                   char *utf8_buffer) noexcept {
 2075|  2.03k|  return get_default_implementation()->convert_utf16le_to_utf8(buf, len,
 2076|  2.03k|                                                               utf8_buffer);
 2077|  2.03k|}
_ZN7simdutf23utf8_length_from_latin1EPKcm:
 2340|    733|                                                   size_t len) noexcept {
 2341|    733|  return get_default_implementation()->utf8_length_from_latin1(buf, len);
 2342|    733|}
_ZN7simdutf22utf8_length_from_utf16EPKDsm:
 2347|  1.00k|                                                  size_t length) noexcept {
 2348|       |  #if SIMDUTF_IS_BIG_ENDIAN
 2349|       |  return utf8_length_from_utf16be(input, length);
 2350|       |  #else
 2351|  1.00k|  return utf8_length_from_utf16le(input, length);
 2352|  1.00k|  #endif
 2353|  1.00k|}
_ZN7simdutf24utf8_length_from_utf16leEPKDsm:
 2363|  1.00k|                                                    size_t length) noexcept {
 2364|  1.00k|  return get_default_implementation()->utf8_length_from_utf16le(input, length);
 2365|  1.00k|}
_ZN7simdutf27convert_latin1_to_utf8_safeEPKcmPcm:
 2568|    733|    const char *buf, size_t len, char *utf8_output, size_t utf8_len) noexcept {
 2569|    733|  const auto start{utf8_output};
 2570|       |
 2571|  2.13k|  while (true) {
  ------------------
  |  Branch (2571:10): [True: 2.13k, Folded]
  ------------------
 2572|       |    // convert_latin1_to_utf8 will never write more than input length * 2
 2573|  2.13k|    auto read_len = detail::min(len, utf8_len >> 1);
 2574|  2.13k|    if (read_len <= 16) {
  ------------------
  |  Branch (2574:9): [True: 733, False: 1.39k]
  ------------------
 2575|    733|      break;
 2576|    733|    }
 2577|       |
 2578|  1.39k|    const auto write_len =
 2579|  1.39k|        simdutf::convert_latin1_to_utf8(buf, read_len, utf8_output);
 2580|       |
 2581|  1.39k|    utf8_output += write_len;
 2582|  1.39k|    utf8_len -= write_len;
 2583|  1.39k|    buf += read_len;
 2584|  1.39k|    len -= read_len;
 2585|  1.39k|  }
 2586|       |
 2587|    733|  utf8_output +=
 2588|    733|      scalar::latin1_to_utf8::convert_safe(buf, len, utf8_output, utf8_len);
 2589|       |
 2590|    733|  return utf8_output - start;
 2591|    733|}
simdutf.cpp:_ZN7simdutf8internalL37get_available_implementation_pointersEv:
  933|      2|get_available_implementation_pointers() {
  934|      2|#if !SIMDUTF_USE_STATIC_INITIALIZATION
  935|      2|  static const std::initializer_list<const implementation *>
  936|      2|      available_implementation_pointers{
  937|      2|  #if SIMDUTF_IMPLEMENTATION_ICELAKE
  938|      2|          get_icelake_singleton(),
  939|      2|  #endif
  940|      2|  #if SIMDUTF_IMPLEMENTATION_HASWELL
  941|      2|          get_haswell_singleton(),
  942|      2|  #endif
  943|      2|  #if SIMDUTF_IMPLEMENTATION_WESTMERE
  944|      2|          get_westmere_singleton(),
  945|      2|  #endif
  946|       |  #if SIMDUTF_IMPLEMENTATION_ARM64
  947|       |          get_arm64_singleton(),
  948|       |  #endif
  949|       |  #if SIMDUTF_IMPLEMENTATION_PPC64
  950|       |          get_ppc64_singleton(),
  951|       |  #endif
  952|       |  #if SIMDUTF_IMPLEMENTATION_RVV
  953|       |          get_rvv_singleton(),
  954|       |  #endif
  955|       |  #if SIMDUTF_IMPLEMENTATION_LASX
  956|       |          get_lasx_singleton(),
  957|       |  #endif
  958|       |  #if SIMDUTF_IMPLEMENTATION_LSX
  959|       |          get_lsx_singleton(),
  960|       |  #endif
  961|      2|  #if SIMDUTF_IMPLEMENTATION_FALLBACK
  962|      2|          get_fallback_singleton(),
  963|      2|  #endif
  964|      2|      };
  965|      2|#endif
  966|      2|  return available_implementation_pointers;
  967|      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|}
_ZN7simdutf8internal49detect_best_supported_implementation_on_first_useC2Ev:
  887|      1|      : implementation("best_supported_detector",
  888|      1|                       "Detects the best supported implementation and sets it",
  889|      1|                       0) {}
_ZN7simdutf26get_default_implementationEv:
 1610|  5.90k|get_default_implementation() {
 1611|  5.90k|  return get_active_implementation();
 1612|  5.90k|}

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

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_110count_onesEm:
   14|  1.25k|simdutf_really_inline long long int count_ones(uint64_t input_num) {
   15|       |  return _popcnt64(input_num);
   16|  1.25k|}

_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_14simd4baseINS2_6simd16ItEEEC2EDv4_x:
   17|  4.94M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEcvRKDv4_xEv:
   19|  4.94M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEC2EDv4_x:
   17|   352k|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEcvRKDv4_xEv:
   19|   352k|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEanES5_:
   53|  1.05M|  simdutf_really_inline Child operator&(const Child other) const {
   54|  1.05M|    return _mm256_and_si256(*this, other);
   55|  1.05M|  }

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd16ItEC2EDv4_x:
   97|  4.94M|      : base16_numeric<uint16_t>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItEC2EDv4_x:
   73|  4.94M|      : base16<T>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6base16ItNS2_6simd16IbEEEC2EDv4_x:
   20|  4.94M|      : base<simd16<T>>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItE4loadEPKt:
   66|   352k|  static simdutf_really_inline simd16<T> load(const T values[8]) {
   67|   352k|    return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(values));
   68|   352k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItE5splatEt:
   58|  1.41M|  static simdutf_really_inline simd16<T> splat(T _value) {
   59|  1.41M|    return _mm256_set1_epi16(_value);
   60|  1.41M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simdeqENS2_6simd16ItEES4_:
   26|   352k|                                               const simd16<T> rhs) {
   27|   352k|    return _mm256_cmpeq_epi16(lhs, rhs);
   28|   352k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd16IbEC2EDv4_x:
   45|   352k|  simdutf_really_inline simd16(const __m256i _value) : base16<bool>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6base16IbNS2_6simd16IbEEEC2EDv4_x:
   20|   352k|      : base<simd16<T>>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd16ItEC2Et:
  100|  1.41M|  simdutf_really_inline simd16(uint16_t _value) : simd16(splat(_value)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItE4zeroEv:
   62|    388|  static simdutf_really_inline simd16<T> zero() {
   63|    388|    return _mm256_setzero_si256();
   64|    388|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd3minENS2_6simd16ItEES4_:
  259|   705k|simd16<uint16_t> min(const simd16<uint16_t> a, simd16<uint16_t> b) {
  260|   705k|  return _mm256_min_epu16(a.value, b.value);
  261|   705k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItEpLENS2_6simd16ItEE:
   87|  1.05M|  simdutf_really_inline simd16<T> &operator+=(const simd16<T> other) {
   88|  1.05M|    *this = *this + other;
   89|  1.05M|    return *static_cast<simd16<T> *>(this);
   90|  1.05M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItEplENS2_6simd16ItEE:
   84|  1.05M|  simdutf_really_inline simd16<T> operator+(const simd16<T> other) const {
   85|  1.05M|    return _mm256_add_epi16(*this, other);
   86|  1.05M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd16ItE3sumEv:
  170|    388|  simdutf_really_inline uint64_t sum() const {
  171|    388|    const auto lo_u16 = _mm256_and_si256(value, _mm256_set1_epi32(0x0000ffff));
  172|    388|    const auto hi_u16 = _mm256_srli_epi32(value, 16);
  173|    388|    const auto sum_u32 = _mm256_add_epi32(lo_u16, hi_u16);
  174|       |
  175|    388|    const auto lo_u32 =
  176|    388|        _mm256_and_si256(sum_u32, _mm256_set1_epi64x(0xffffffff));
  177|    388|    const auto hi_u32 = _mm256_srli_epi64(sum_u32, 32);
  178|    388|    const auto sum_u64 = _mm256_add_epi64(lo_u32, hi_u32);
  179|       |
  180|    388|    return uint64_t(_mm256_extract_epi64(sum_u64, 0)) +
  181|    388|           uint64_t(_mm256_extract_epi64(sum_u64, 1)) +
  182|    388|           uint64_t(_mm256_extract_epi64(sum_u64, 2)) +
  183|       |           uint64_t(_mm256_extract_epi64(sum_u64, 3));
  184|    388|  }

_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) {}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_110count_onesEm:
   14|  3.00k|simdutf_really_inline long long int count_ones(uint64_t input_num) {
   15|       |  return _popcnt64(input_num);
   16|  3.00k|}

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

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEC2EDv2_x:
   16|  13.4M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEcvRKDv2_xEv:
   18|  13.4M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEC2EDv2_x:
   16|   959k|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEcvRKDv2_xEv:
   18|   959k|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEanES5_:
   45|  2.87M|  simdutf_really_inline Child operator&(const Child other) const {
   46|  2.87M|    return _mm_and_si128(*this, other);
   47|  2.87M|  }

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd16ItEC2EDv2_x:
   79|  13.4M|      : base16_numeric<uint16_t>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItEC2EDv2_x:
   54|  13.4M|      : base16<T>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6base16ItNS2_6simd16IbEEEC2EDv2_x:
    8|  13.4M|      : base<simd16<T>>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItE4loadEPKt:
   47|   959k|  static simdutf_really_inline simd16<T> load(const T values[8]) {
   48|   959k|    return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
   49|   959k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItE5splatEt:
   41|  3.83M|  static simdutf_really_inline simd16<T> splat(T _value) {
   42|  3.83M|    return _mm_set1_epi16(_value);
   43|  3.83M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdeqENS2_6simd16ItEES4_:
   11|   959k|                                               const simd16<T> rhs) {
   12|   959k|    return _mm_cmpeq_epi16(lhs, rhs);
   13|   959k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd16IbEC2EDv2_x:
   28|   959k|  simdutf_really_inline simd16(const __m128i _value) : base16<bool>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6base16IbNS2_6simd16IbEEEC2EDv2_x:
    8|   959k|      : base<simd16<T>>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd16ItEC2Et:
   82|  3.83M|  simdutf_really_inline simd16(uint16_t _value) : simd16(splat(_value)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItE4zeroEv:
   45|    412|  static simdutf_really_inline simd16<T> zero() { return _mm_setzero_si128(); }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd3minENS2_6simd16ItEES4_:
  240|  1.91M|simd16<uint16_t> min(const simd16<uint16_t> a, simd16<uint16_t> b) {
  241|  1.91M|  return _mm_min_epu16(a.value, b.value);
  242|  1.91M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItEpLENS2_6simd16ItEE:
   68|  2.87M|  simdutf_really_inline simd16<T> &operator+=(const simd16<T> other) {
   69|  2.87M|    *this = *this + other;
   70|  2.87M|    return *static_cast<simd16<T> *>(this);
   71|  2.87M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItEplENS2_6simd16ItEE:
   65|  2.87M|  simdutf_really_inline simd16<T> operator+(const simd16<T> other) const {
   66|  2.87M|    return _mm_add_epi16(*this, other);
   67|  2.87M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd16ItE3sumEv:
  134|    412|  simdutf_really_inline uint64_t sum() const {
  135|    412|    const auto lo_u16 = _mm_and_si128(value, _mm_set1_epi32(0x0000ffff));
  136|    412|    const auto hi_u16 = _mm_srli_epi32(value, 16);
  137|    412|    const auto sum_u32 = _mm_add_epi32(lo_u16, hi_u16);
  138|       |
  139|    412|    const auto lo_u32 = _mm_and_si128(sum_u32, _mm_set1_epi64x(0xffffffff));
  140|    412|    const auto hi_u32 = _mm_srli_epi64(sum_u32, 32);
  141|    412|    const auto sum_u64 = _mm_add_epi64(lo_u32, hi_u32);
  142|       |
  143|    412|    return uint64_t(_mm_extract_epi64(sum_u64, 0)) +
  144|       |           uint64_t(_mm_extract_epi64(sum_u64, 1));
  145|    412|  }

_ZNK7simdutf8westmere14implementation22convert_latin1_to_utf8EPKcmPc:
  441|    877|    const char *buf, size_t len, char *utf8_output) const noexcept {
  442|       |
  443|    877|  std::pair<const char *, char *> ret =
  444|    877|      sse_convert_latin1_to_utf8(buf, len, utf8_output);
  445|    877|  size_t converted_chars = ret.second - utf8_output;
  446|       |
  447|    877|  if (ret.first != buf + len) {
  ------------------
  |  Branch (447:7): [True: 745, False: 132]
  ------------------
  448|    745|    const size_t scalar_converted_chars = scalar::latin1_to_utf8::convert(
  449|    745|        ret.first, len - (ret.first - buf), ret.second);
  450|    745|    converted_chars += scalar_converted_chars;
  451|    745|  }
  452|       |
  453|    877|  return converted_chars;
  454|    877|}
_ZNK7simdutf8westmere14implementation23convert_utf16le_to_utf8EPKDsmPc:
  709|    755|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  710|    755|  std::pair<const char16_t *, char *> ret =
  711|    755|      sse_convert_utf16_to_utf8<endianness::LITTLE>(buf, len, utf8_output);
  712|    755|  if (ret.first == nullptr) {
  ------------------
  |  Branch (712:7): [True: 115, False: 640]
  ------------------
  713|    115|    return 0;
  714|    115|  }
  715|    640|  size_t saved_bytes = ret.second - utf8_output;
  716|    640|  if (ret.first != buf + len) {
  ------------------
  |  Branch (716:7): [True: 636, False: 4]
  ------------------
  717|    636|    const size_t scalar_saved_bytes =
  718|    636|        scalar::utf16_to_utf8::convert<endianness::LITTLE>(
  719|    636|            ret.first, len - (ret.first - buf), ret.second);
  720|    636|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (720:9): [True: 140, False: 496]
  ------------------
  721|    140|      return 0;
  722|    140|    }
  723|    496|    saved_bytes += scalar_saved_bytes;
  724|    496|  }
  725|    500|  return saved_bytes;
  726|    640|}
_ZNK7simdutf8westmere14implementation24utf8_length_from_utf16leEPKDsm:
 1135|    395|    const char16_t *input, size_t length) const noexcept {
 1136|    395|  return utf16::utf8_length_from_utf16_bytemask<endianness::LITTLE>(input,
 1137|    395|                                                                    length);
 1138|    395|}
_ZNK7simdutf8westmere14implementation23utf8_length_from_latin1EPKcm:
 1148|    285|    const char *input, size_t len) const noexcept {
 1149|    285|  const uint8_t *str = reinterpret_cast<const uint8_t *>(input);
 1150|    285|  size_t answer = len / sizeof(__m128i) * sizeof(__m128i);
 1151|    285|  size_t i = 0;
 1152|    285|  if (answer >= 2048) { // long strings optimization
  ------------------
  |  Branch (1152:7): [True: 133, False: 152]
  ------------------
 1153|    133|    __m128i two_64bits = _mm_setzero_si128();
 1154|  5.09k|    while (i + sizeof(__m128i) <= len) {
  ------------------
  |  Branch (1154:12): [True: 4.96k, False: 133]
  ------------------
 1155|  4.96k|      __m128i runner = _mm_setzero_si128();
 1156|  4.96k|      size_t iterations = (len - i) / sizeof(__m128i);
 1157|  4.96k|      if (iterations > 255) {
  ------------------
  |  Branch (1157:11): [True: 4.82k, False: 133]
  ------------------
 1158|  4.82k|        iterations = 255;
 1159|  4.82k|      }
 1160|  4.96k|      size_t max_i = i + iterations * sizeof(__m128i) - sizeof(__m128i);
 1161|   312k|      for (; i + 4 * sizeof(__m128i) <= max_i; i += 4 * sizeof(__m128i)) {
  ------------------
  |  Branch (1161:14): [True: 307k, False: 4.96k]
  ------------------
 1162|   307k|        __m128i input1 = _mm_loadu_si128((const __m128i *)(str + i));
 1163|   307k|        __m128i input2 =
 1164|   307k|            _mm_loadu_si128((const __m128i *)(str + i + sizeof(__m128i)));
 1165|   307k|        __m128i input3 =
 1166|   307k|            _mm_loadu_si128((const __m128i *)(str + i + 2 * sizeof(__m128i)));
 1167|   307k|        __m128i input4 =
 1168|   307k|            _mm_loadu_si128((const __m128i *)(str + i + 3 * sizeof(__m128i)));
 1169|   307k|        __m128i input12 =
 1170|   307k|            _mm_add_epi8(_mm_cmpgt_epi8(_mm_setzero_si128(), input1),
 1171|   307k|                         _mm_cmpgt_epi8(_mm_setzero_si128(), input2));
 1172|   307k|        __m128i input34 =
 1173|   307k|            _mm_add_epi8(_mm_cmpgt_epi8(_mm_setzero_si128(), input3),
 1174|   307k|                         _mm_cmpgt_epi8(_mm_setzero_si128(), input4));
 1175|   307k|        __m128i input1234 = _mm_add_epi8(input12, input34);
 1176|   307k|        runner = _mm_sub_epi8(runner, input1234);
 1177|   307k|      }
 1178|  19.7k|      for (; i <= max_i; i += sizeof(__m128i)) {
  ------------------
  |  Branch (1178:14): [True: 14.8k, False: 4.96k]
  ------------------
 1179|  14.8k|        __m128i more_input = _mm_loadu_si128((const __m128i *)(str + i));
 1180|  14.8k|        runner = _mm_sub_epi8(runner,
 1181|  14.8k|                              _mm_cmpgt_epi8(_mm_setzero_si128(), more_input));
 1182|  14.8k|      }
 1183|  4.96k|      two_64bits =
 1184|  4.96k|          _mm_add_epi64(two_64bits, _mm_sad_epu8(runner, _mm_setzero_si128()));
 1185|  4.96k|    }
 1186|    133|    answer +=
 1187|    133|        _mm_extract_epi64(two_64bits, 0) + _mm_extract_epi64(two_64bits, 1);
 1188|    152|  } else if (answer > 0) { // short string optimization
  ------------------
  |  Branch (1188:14): [True: 108, False: 44]
  ------------------
 1189|  1.58k|    for (; i + 2 * sizeof(__m128i) <= len; i += 2 * sizeof(__m128i)) {
  ------------------
  |  Branch (1189:12): [True: 1.47k, False: 108]
  ------------------
 1190|  1.47k|      __m128i latin = _mm_loadu_si128((const __m128i *)(input + i));
 1191|  1.47k|      uint16_t non_ascii = (uint16_t)_mm_movemask_epi8(latin);
 1192|  1.47k|      answer += count_ones(non_ascii);
 1193|  1.47k|      latin = _mm_loadu_si128((const __m128i *)(input + i) + 1);
 1194|  1.47k|      non_ascii = (uint16_t)_mm_movemask_epi8(latin);
 1195|  1.47k|      answer += count_ones(non_ascii);
 1196|  1.47k|    }
 1197|    164|    for (; i + sizeof(__m128i) <= len; i += sizeof(__m128i)) {
  ------------------
  |  Branch (1197:12): [True: 56, False: 108]
  ------------------
 1198|     56|      __m128i latin = _mm_loadu_si128((const __m128i *)(input + i));
 1199|     56|      uint16_t non_ascii = (uint16_t)_mm_movemask_epi8(latin);
 1200|     56|      answer += count_ones(non_ascii);
 1201|     56|    }
 1202|    108|  }
 1203|    285|  return answer + scalar::latin1::utf8_length_from_latin1(
 1204|    285|                      reinterpret_cast<const char *>(str + i), len - i);
 1205|    285|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_18internal8westmere26write_v_u16_11bits_to_utf8EDv2_xRPcS4_S4_:
   57|  1.17M|                                       const __m128i v_ff80) {
   58|       |  // no bits set above 7th bit
   59|  1.17M|  const __m128i one_byte_bytemask =
   60|  1.17M|      _mm_cmpeq_epi16(_mm_and_si128(v_u16, v_ff80), v_0000);
   61|  1.17M|  const uint16_t one_byte_bitmask =
   62|  1.17M|      static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
   63|       |
   64|  1.17M|  write_v_u16_11bits_to_utf8(v_u16, utf8_output, one_byte_bytemask,
   65|  1.17M|                             one_byte_bitmask);
   66|  1.17M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_18internal8westmere26write_v_u16_11bits_to_utf8EDv2_xRPcS4_t:
   10|  1.17M|                                       const uint16_t one_byte_bitmask) {
   11|       |  // 0b1100_0000_1000_0000
   12|  1.17M|  const __m128i v_c080 = _mm_set1_epi16((int16_t)0xc080);
   13|       |  // 0b0001_1111_0000_0000
   14|  1.17M|  const __m128i v_1f00 = _mm_set1_epi16((int16_t)0x1f00);
   15|       |  // 0b0000_0000_0011_1111
   16|  1.17M|  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|  1.17M|  const __m128i t0 = _mm_slli_epi16(v_u16, 2);
   24|       |  // t1 = [000a|aaaa|0000|0000]
   25|  1.17M|  const __m128i t1 = _mm_and_si128(t0, v_1f00);
   26|       |  // t2 = [0000|0000|00bb|bbbb]
   27|  1.17M|  const __m128i t2 = _mm_and_si128(v_u16, v_003f);
   28|       |  // t3 = [000a|aaaa|00bb|bbbb]
   29|  1.17M|  const __m128i t3 = _mm_or_si128(t1, t2);
   30|       |  // t4 = [110a|aaaa|10bb|bbbb]
   31|  1.17M|  const __m128i t4 = _mm_or_si128(t3, v_c080);
   32|       |
   33|       |  // 2. merge ASCII and 2-byte codewords
   34|  1.17M|  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|  1.17M|  const uint16_t m0 = one_byte_bitmask & 0x5555;      // m0 = 0h0g0f0e0d0c0b0a
   40|  1.17M|  const uint16_t m1 = static_cast<uint16_t>(m0 >> 7); // m1 = 00000000h0g0f0e0
   41|  1.17M|  const uint8_t m2 = static_cast<uint8_t>((m0 | m1) & 0xff); // m2 = hdgcfbea
   42|       |  // 4. pack the bytes
   43|  1.17M|  const uint8_t *row =
   44|  1.17M|      &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0];
   45|  1.17M|  const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
   46|  1.17M|  const __m128i utf8_packed = _mm_shuffle_epi8(utf8_unpacked, shuffle);
   47|       |
   48|       |  // 5. store bytes
   49|  1.17M|  _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   50|       |
   51|       |  // 6. adjust pointers
   52|  1.17M|  utf8_output += row[0];
   53|  1.17M|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_126sse_convert_latin1_to_utf8EPKcmPc:
    3|    877|                           const size_t latin_input_length, char *utf8_output) {
    4|    877|  const char *end = latin_input + latin_input_length;
    5|       |
    6|    877|  const __m128i v_0000 = _mm_setzero_si128();
    7|       |  // 0b1000_0000
    8|    877|  const __m128i v_80 = _mm_set1_epi8((uint8_t)0x80);
    9|       |  // 0b1111_1111_1000_0000
   10|    877|  const __m128i v_ff80 = _mm_set1_epi16((uint16_t)0xff80);
   11|       |
   12|    877|  const __m128i latin_1_half_into_u16_byte_mask =
   13|    877|      _mm_setr_epi8(0, '\x80', 1, '\x80', 2, '\x80', 3, '\x80', 4, '\x80', 5,
   14|    877|                    '\x80', 6, '\x80', 7, '\x80');
   15|       |
   16|    877|  const __m128i latin_2_half_into_u16_byte_mask =
   17|    877|      _mm_setr_epi8(8, '\x80', 9, '\x80', 10, '\x80', 11, '\x80', 12, '\x80',
   18|    877|                    13, '\x80', 14, '\x80', 15, '\x80');
   19|       |
   20|       |  // each latin1 takes 1-2 utf8 bytes
   21|       |  // slow path writes useful 8-15 bytes twice (eagerly writes 16 bytes and then
   22|       |  // adjust the pointer) so the last write can exceed the utf8_output size by
   23|       |  // 8-1 bytes by reserving 8 extra input bytes, we expect the output to have
   24|       |  // 8-16 bytes free
   25|  1.33M|  while (end - latin_input >= 16 + 8) {
  ------------------
  |  Branch (25:10): [True: 1.33M, False: 877]
  ------------------
   26|       |    // Load 16 Latin1 characters (16 bytes) into a 128-bit register
   27|  1.33M|    __m128i v_latin = _mm_loadu_si128((__m128i *)latin_input);
   28|       |
   29|  1.33M|    if (_mm_testz_si128(v_latin, v_80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (29:9): [True: 747k, False: 587k]
  ------------------
   30|   747k|      _mm_storeu_si128((__m128i *)utf8_output, v_latin);
   31|   747k|      latin_input += 16;
   32|   747k|      utf8_output += 16;
   33|   747k|      continue;
   34|   747k|    }
   35|       |
   36|       |    // assuming a/b are bytes and A/B are uint16 of the same value
   37|       |    // aaaa_aaaa_bbbb_bbbb -> AAAA_AAAA
   38|   587k|    __m128i v_u16_latin_1_half =
   39|   587k|        _mm_shuffle_epi8(v_latin, latin_1_half_into_u16_byte_mask);
   40|       |    // aaaa_aaaa_bbbb_bbbb -> BBBB_BBBB
   41|   587k|    __m128i v_u16_latin_2_half =
   42|   587k|        _mm_shuffle_epi8(v_latin, latin_2_half_into_u16_byte_mask);
   43|       |
   44|   587k|    internal::westmere::write_v_u16_11bits_to_utf8(v_u16_latin_1_half,
   45|   587k|                                                   utf8_output, v_0000, v_ff80);
   46|   587k|    internal::westmere::write_v_u16_11bits_to_utf8(v_u16_latin_2_half,
   47|   587k|                                                   utf8_output, v_0000, v_ff80);
   48|   587k|    latin_input += 16;
   49|   587k|  }
   50|       |
   51|    877|  if (end - latin_input >= 16) {
  ------------------
  |  Branch (51:7): [True: 467, False: 410]
  ------------------
   52|       |    // Load 16 Latin1 characters (16 bytes) into a 128-bit register
   53|    467|    __m128i v_latin = _mm_loadu_si128((__m128i *)latin_input);
   54|       |
   55|    467|    if (_mm_testz_si128(v_latin, v_80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (55:9): [True: 258, False: 209]
  ------------------
   56|    258|      _mm_storeu_si128((__m128i *)utf8_output, v_latin);
   57|    258|      latin_input += 16;
   58|    258|      utf8_output += 16;
   59|    258|    } else {
   60|       |      // assuming a/b are bytes and A/B are uint16 of the same value
   61|       |      // aaaa_aaaa_bbbb_bbbb -> AAAA_AAAA
   62|    209|      __m128i v_u16_latin_1_half =
   63|    209|          _mm_shuffle_epi8(v_latin, latin_1_half_into_u16_byte_mask);
   64|    209|      internal::westmere::write_v_u16_11bits_to_utf8(
   65|    209|          v_u16_latin_1_half, utf8_output, v_0000, v_ff80);
   66|    209|      latin_input += 8;
   67|    209|    }
   68|    467|  }
   69|       |
   70|    877|  return std::make_pair(latin_input, utf8_output);
   71|    877|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_125sse_convert_utf16_to_utf8ILNS_10endiannessE0EEENSt3__14pairIPKDsPcEES7_mS8_:
   56|    755|sse_convert_utf16_to_utf8(const char16_t *buf, size_t len, char *utf8_output) {
   57|       |
   58|    755|  const char16_t *end = buf + len;
   59|       |
   60|    755|  const __m128i v_0000 = _mm_setzero_si128();
   61|    755|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
   62|    755|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
   63|    755|  const size_t safety_margin =
   64|    755|      12; // to avoid overruns, see issue
   65|       |          // https://github.com/simdutf/simdutf/issues/92
   66|       |
   67|   186k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (67:10): [True: 185k, False: 640]
  ------------------
   68|   185k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
   69|   185k|    if (big_endian) {
  ------------------
  |  Branch (69:9): [Folded, False: 185k]
  ------------------
   70|      0|      const __m128i swap =
   71|      0|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   72|      0|      in = _mm_shuffle_epi8(in, swap);
   73|      0|    }
   74|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
   75|   185k|    const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80);
   76|   185k|    if (_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (76:9): [True: 103k, False: 82.2k]
  ------------------
   77|   103k|      __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
   78|   103k|      if (big_endian) {
  ------------------
  |  Branch (78:11): [Folded, False: 103k]
  ------------------
   79|      0|        const __m128i swap =
   80|      0|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   81|      0|        nextin = _mm_shuffle_epi8(nextin, swap);
   82|      0|      }
   83|   103k|      if (!_mm_testz_si128(nextin, v_ff80)) {
  ------------------
  |  Branch (83:11): [True: 1.39k, False: 102k]
  ------------------
   84|       |        // 1. pack the bytes
   85|       |        // obviously suboptimal.
   86|  1.39k|        const __m128i utf8_packed = _mm_packus_epi16(in, in);
   87|       |        // 2. store (16 bytes)
   88|  1.39k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   89|       |        // 3. adjust pointers
   90|  1.39k|        buf += 8;
   91|  1.39k|        utf8_output += 8;
   92|  1.39k|        in = nextin;
   93|   102k|      } else {
   94|       |        // 1. pack the bytes
   95|       |        // obviously suboptimal.
   96|   102k|        const __m128i utf8_packed = _mm_packus_epi16(in, nextin);
   97|       |        // 2. store (16 bytes)
   98|   102k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   99|       |        // 3. adjust pointers
  100|   102k|        buf += 16;
  101|   102k|        utf8_output += 16;
  102|   102k|        continue; // we are done for this round!
  103|   102k|      }
  104|   103k|    }
  105|       |
  106|       |    // no bits set above 7th bit
  107|  83.6k|    const __m128i one_byte_bytemask =
  108|  83.6k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000);
  109|  83.6k|    const uint16_t one_byte_bitmask =
  110|  83.6k|        static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
  111|       |
  112|       |    // no bits set above 11th bit
  113|  83.6k|    const __m128i one_or_two_bytes_bytemask =
  114|  83.6k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000);
  115|  83.6k|    const uint16_t one_or_two_bytes_bitmask =
  116|  83.6k|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  117|       |
  118|  83.6k|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (118:9): [True: 4.03k, False: 79.5k]
  ------------------
  119|  4.03k|      internal::westmere::write_v_u16_11bits_to_utf8(
  120|  4.03k|          in, utf8_output, one_byte_bytemask, one_byte_bitmask);
  121|  4.03k|      buf += 8;
  122|  4.03k|      continue;
  123|  4.03k|    }
  124|       |
  125|       |    // 1. Check if there are any surrogate word in the input chunk.
  126|       |    //    We have also deal with situation when there is a surrogate word
  127|       |    //    at the end of a chunk.
  128|  79.5k|    const __m128i surrogates_bytemask =
  129|  79.5k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800);
  130|       |
  131|       |    // bitmask = 0x0000 if there are no surrogates
  132|       |    //         = 0xc000 if the last word is a surrogate
  133|  79.5k|    const uint16_t surrogates_bitmask =
  134|  79.5k|        static_cast<uint16_t>(_mm_movemask_epi8(surrogates_bytemask));
  135|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
  136|       |    // However, it is likely an uncommon occurrence.
  137|  79.5k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (137:9): [True: 78.0k, False: 1.47k]
  ------------------
  138|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  139|  78.0k|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  140|  78.0k|                                              0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
  141|       |
  142|       |      /* In this branch we handle three cases:
  143|       |         1. [0000|0000|0ccc|cccc] => [0ccc|cccc]                           -
  144|       |        single UFT-8 byte
  145|       |         2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc]              - two
  146|       |        UTF-8 bytes
  147|       |         3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] -
  148|       |        three UTF-8 bytes
  149|       |
  150|       |        We expand the input word (16-bit) into two code units (32-bit), thus
  151|       |        we have room for four bytes. However, we need five distinct bit
  152|       |        layouts. Note that the last byte in cases #2 and #3 is the same.
  153|       |
  154|       |        We precompute byte 1 for case #1 and the common byte for cases #2 & #3
  155|       |        in register t2.
  156|       |
  157|       |        We precompute byte 1 for case #3 and -- **conditionally** -- precompute
  158|       |        either byte 1 for case #2 or byte 2 for case #3. Note that they
  159|       |        differ by exactly one bit.
  160|       |
  161|       |        Finally from these two code units we build proper UTF-8 sequence, taking
  162|       |        into account the case (i.e, the number of bytes to write).
  163|       |      */
  164|       |      /**
  165|       |       * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
  166|       |       * t2 => [0ccc|cccc] [10cc|cccc]
  167|       |       * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
  168|       |       */
  169|  78.0k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  170|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  171|  78.0k|      const __m128i t0 = _mm_shuffle_epi8(in, dup_even);
  172|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  173|  78.0k|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  169|  78.0k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  174|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  175|  78.0k|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  169|  78.0k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  176|       |
  177|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  178|  78.0k|      const __m128i s0 = _mm_srli_epi16(in, 4);
  179|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  180|  78.0k|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  169|  78.0k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  181|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  182|  78.0k|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  169|  78.0k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  183|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  184|  78.0k|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  169|  78.0k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  185|  78.0k|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  186|  78.0k|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  169|  78.0k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  187|  78.0k|      const __m128i s4 = _mm_xor_si128(s3, m0);
  188|  78.0k|#undef simdutf_vec
  189|       |
  190|       |      // 4. expand code units 16-bit => 32-bit
  191|  78.0k|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  192|  78.0k|      const __m128i out1 = _mm_unpackhi_epi16(t2, s4);
  193|       |
  194|       |      // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
  195|  78.0k|      const uint16_t mask =
  196|  78.0k|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  197|  78.0k|      if (mask == 0) {
  ------------------
  |  Branch (197:11): [True: 71.5k, False: 6.56k]
  ------------------
  198|       |        // We only have three-byte code units. Use fast path.
  199|  71.5k|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  200|  71.5k|                                              15, 13, -1, -1, -1, -1);
  201|  71.5k|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  202|  71.5k|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  203|  71.5k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  204|  71.5k|        utf8_output += 12;
  205|  71.5k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  206|  71.5k|        utf8_output += 12;
  207|  71.5k|        buf += 8;
  208|  71.5k|        continue;
  209|  71.5k|      }
  210|  6.56k|      const uint8_t mask0 = uint8_t(mask);
  211|       |
  212|  6.56k|      const uint8_t *row0 =
  213|  6.56k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  214|  6.56k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  215|  6.56k|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  216|       |
  217|  6.56k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  218|       |
  219|  6.56k|      const uint8_t *row1 =
  220|  6.56k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  221|  6.56k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  222|  6.56k|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  223|       |
  224|  6.56k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  225|  6.56k|      utf8_output += row0[0];
  226|  6.56k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  227|  6.56k|      utf8_output += row1[0];
  228|       |
  229|  6.56k|      buf += 8;
  230|       |      // surrogate pair(s) in a register
  231|  6.56k|    } else {
  232|       |      // Let us do a scalar fallback.
  233|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
  234|       |      // in the presence of surrogate pairs may require non-trivial tables.
  235|  1.47k|      size_t forward = 15;
  236|  1.47k|      size_t k = 0;
  237|  1.47k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (237:11): [True: 0, False: 1.47k]
  ------------------
  238|      0|        forward = size_t(end - buf - 1);
  239|      0|      }
  240|  19.4k|      for (; k < forward; k++) {
  ------------------
  |  Branch (240:14): [True: 18.1k, False: 1.35k]
  ------------------
  241|  18.1k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  242|  18.1k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (242:13): [True: 7.43k, False: 10.7k]
  ------------------
  243|  7.43k|          *utf8_output++ = char(word);
  244|  10.7k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (244:20): [True: 998, False: 9.71k]
  ------------------
  245|    998|          *utf8_output++ = char((word >> 6) | 0b11000000);
  246|    998|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  247|  9.71k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (247:20): [True: 6.68k, False: 3.02k]
  ------------------
  248|  6.68k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  249|  6.68k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  250|  6.68k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  251|  6.68k|        } else {
  252|       |          // must be a surrogate pair
  253|  3.02k|          uint16_t diff = uint16_t(word - 0xD800);
  254|  3.02k|          uint16_t next_word =
  255|  3.02k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  256|  3.02k|          k++;
  257|  3.02k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  258|  3.02k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (258:15): [True: 115, False: 2.91k]
  ------------------
  259|    115|            return std::make_pair(nullptr, utf8_output);
  260|    115|          }
  261|  2.91k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  262|  2.91k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  263|  2.91k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  264|  2.91k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  265|  2.91k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  266|  2.91k|        }
  267|  18.1k|      }
  268|  1.35k|      buf += k;
  269|  1.35k|    }
  270|  79.5k|  } // while
  271|       |
  272|    640|  return std::make_pair(buf, utf8_output);
  273|    755|}

_ZN7simdutf22utf8_length_from_utf16ENSt3__14spanIKDsLm18446744073709551615EEE:
 2964|  1.00k|utf8_length_from_utf16(std::span<const char16_t> valid_utf16_input) noexcept {
 2965|       |    #if SIMDUTF_CPLUSPLUS23
 2966|       |  if consteval {
 2967|       |    return scalar::utf16::utf8_length_from_utf16<endianness::NATIVE>(
 2968|       |        valid_utf16_input.data(), valid_utf16_input.size());
 2969|       |  } else
 2970|       |    #endif
 2971|  1.00k|  {
 2972|  1.00k|    return utf8_length_from_utf16(valid_utf16_input.data(),
 2973|  1.00k|                                  valid_utf16_input.size());
 2974|  1.00k|  }
 2975|  1.00k|}
safe_conversion.cpp:_ZN7simdutf6detail12_GLOBAL__N_13minEmm:
   59|  1.00k|constexpr std::size_t min(std::size_t a, std::size_t b) {
   60|  1.00k|  return a < b ? a : b;
  ------------------
  |  Branch (60:10): [True: 130, False: 875]
  ------------------
   61|  1.00k|}
_ZN7simdutf27convert_latin1_to_utf8_safeITkNS_6detail23input_span_of_byte_likeENSt3__14spanIKhLm18446744073709551615EEETkNS1_24output_span_of_byte_likeERNS2_6vectorIcNS2_9allocatorIcEEEEEEmRKT_OT0_:
  880|    733|    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
  881|       |      // implementation note: outputspan is a forwarding ref to avoid copying
  882|       |      // and allow both lvalues and rvalues. std::span can be copied without
  883|       |      // problems, but std::vector should not, and this function should accept
  884|       |      // both. it will allow using an owning rvalue ref (example: passing a
  885|       |      // temporary std::string) as output, but the user will quickly find out
  886|       |      // that he has no way of getting the data out of the object in that case.
  887|       |    #if SIMDUTF_CPLUSPLUS23
  888|       |  if consteval {
  889|       |    return scalar::latin1_to_utf8::convert_safe_constexpr(
  890|       |        input.data(), input.size(), utf8_output.data(), utf8_output.size());
  891|       |  } else
  892|       |    #endif
  893|    733|  {
  894|    733|    return convert_latin1_to_utf8_safe(
  895|    733|        reinterpret_cast<const char *>(input.data()), input.size(),
  896|    733|        reinterpret_cast<char *>(utf8_output.data()), utf8_output.size());
  897|    733|  }
  898|    733|}
_ZN7simdutf23utf8_length_from_latin1ITkNS_6detail23input_span_of_byte_likeENSt3__14spanIKhLm18446744073709551615EEEEEmRKT_:
 1663|    733|    const detail::input_span_of_byte_like auto &latin1_input) noexcept {
 1664|       |    #if SIMDUTF_CPLUSPLUS23
 1665|       |  if consteval {
 1666|       |    return scalar::latin1_to_utf8::utf8_length_from_latin1(latin1_input.data(),
 1667|       |                                                           latin1_input.size());
 1668|       |  } else
 1669|       |    #endif
 1670|    733|  {
 1671|    733|    return utf8_length_from_latin1(
 1672|    733|        reinterpret_cast<const char *>(latin1_input.data()),
 1673|    733|        latin1_input.size());
 1674|    733|  }
 1675|    733|}
_ZN7simdutf22convert_latin1_to_utf8ITkNS_6detail23input_span_of_byte_likeENSt3__14spanIKhLm18446744073709551615EEETkNS1_24output_span_of_byte_likeERNS2_6vectorIcNS2_9allocatorIcEEEEEEmRKT_OT0_:
  837|    733|    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
  838|       |    #if SIMDUTF_CPLUSPLUS23
  839|       |  if consteval {
  840|       |    return scalar::latin1_to_utf8::convert(
  841|       |        detail::constexpr_cast_ptr<char>(latin1_input.data()),
  842|       |        latin1_input.size(),
  843|       |        detail::constexpr_cast_writeptr<char>(utf8_output.data()));
  844|       |  } else
  845|       |    #endif
  846|    733|  {
  847|    733|    return convert_latin1_to_utf8(
  848|    733|        reinterpret_cast<const char *>(latin1_input.data()),
  849|    733|        latin1_input.size(), reinterpret_cast<char *>(utf8_output.data()));
  850|    733|  }
  851|    733|}
_ZN7simdutf26convert_utf16_to_utf8_safeITkNS_6detail24output_span_of_byte_likeERNSt3__16vectorIcNS2_9allocatorIcEEEEEEmNS2_4spanIKDsLm18446744073709551615EEEOT_:
 1855|  1.00k|    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
 1856|       |      // implementation note: outputspan is a forwarding ref to avoid copying
 1857|       |      // and allow both lvalues and rvalues. std::span can be copied without
 1858|       |      // problems, but std::vector should not, and this function should accept
 1859|       |      // both. it will allow using an owning rvalue ref (example: passing a
 1860|       |      // temporary std::string) as output, but the user will quickly find out
 1861|       |      // that he has no way of getting the data out of the object in that case.
 1862|       |    #if SIMDUTF_CPLUSPLUS23
 1863|       |  if consteval {
 1864|       |    const full_result r =
 1865|       |        scalar::utf16_to_utf8::convert_with_errors<endianness::NATIVE, true>(
 1866|       |            utf16_input.data(), utf16_input.size(), utf8_output.data(),
 1867|       |            utf8_output.size());
 1868|       |    if (r.error != error_code::SUCCESS &&
 1869|       |        r.error != error_code::OUTPUT_BUFFER_TOO_SMALL) {
 1870|       |      return 0;
 1871|       |    }
 1872|       |    return r.output_count;
 1873|       |  } else
 1874|       |    #endif
 1875|  1.00k|  {
 1876|  1.00k|    return convert_utf16_to_utf8_safe(
 1877|  1.00k|        utf16_input.data(), utf16_input.size(),
 1878|  1.00k|        reinterpret_cast<char *>(utf8_output.data()), utf8_output.size());
 1879|  1.00k|  }
 1880|  1.00k|}
_ZN7simdutf21convert_utf16_to_utf8ITkNS_6detail24output_span_of_byte_likeERNSt3__16vectorIcNS2_9allocatorIcEEEEEEmNS2_4spanIKDsLm18446744073709551615EEEOT_:
 1810|  1.00k|    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
 1811|       |    #if SIMDUTF_CPLUSPLUS23
 1812|       |  if consteval {
 1813|       |    return scalar::utf16_to_utf8::convert<endianness::NATIVE>(
 1814|       |        utf16_input.data(), utf16_input.size(), utf8_output.data());
 1815|       |  } else
 1816|       |    #endif
 1817|  1.00k|  {
 1818|  1.00k|    return convert_utf16_to_utf8(utf16_input.data(), utf16_input.size(),
 1819|  1.00k|                                 reinterpret_cast<char *>(utf8_output.data()));
 1820|  1.00k|  }
 1821|  1.00k|}
_ZN7simdutf8internal10atomic_ptrIKNS_14implementationEEaSEPS3_:
 7161|  1.73k|  atomic_ptr &operator=(T *_ptr) {
 7162|  1.73k|    ptr = _ptr;
 7163|  1.73k|    return *this;
 7164|  1.73k|  }

