_Z18populate_functionsv:
  548|      1|const auto populate_functions() {
  549|      1|  using I = simdutf::implementation;
  550|      1|  using FuzzSignature = void (*)(std::span<const char>);
  551|       |
  552|      1|#define ADD(lenfunc, conversionfunc)                                           \
  553|      1|  FuzzSignature {                                                              \
  554|      1|    +[](std::span<const char> chardata) {                                      \
  555|      1|      const auto c =                                                           \
  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  562|      1|      c.fuzz(chardata);                                                        \
  563|      1|    }                                                                          \
  564|      1|  }
  565|       |
  566|      1|  return std::array{
  567|       |      // all these cases require valid input for invoking the convert function
  568|       |
  569|       |      // see #493
  570|       |      // IGNORE(latin1_length_from_utf16, convert_valid_utf16be_to_latin1),
  571|      1|      ADD(utf32_length_from_utf16be, convert_valid_utf16be_to_utf32),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  572|      1|      ADD(utf8_length_from_utf16be, convert_valid_utf16be_to_utf8),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  573|       |
  574|       |      //  see #493
  575|       |      // IGNORE(latin1_length_from_utf16, convert_valid_utf16le_to_latin1),
  576|      1|      ADD(utf32_length_from_utf16le, convert_valid_utf16le_to_utf32),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  577|      1|      ADD(utf8_length_from_utf16le, convert_valid_utf16le_to_utf8),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  578|       |
  579|       |      // see #493
  580|       |      // IGNORE(latin1_length_from_utf32, convert_valid_utf32_to_latin1),
  581|      1|      ADD(utf16_length_from_utf32, convert_valid_utf32_to_utf16be),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  582|      1|      ADD(utf16_length_from_utf32, convert_valid_utf32_to_utf16le),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  583|      1|      ADD(utf8_length_from_utf32, convert_valid_utf32_to_utf8),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  584|       |
  585|       |      // see #493
  586|       |      // IGNORE(latin1_length_from_utf8, convert_valid_utf8_to_latin1),
  587|      1|      ADD(utf16_length_from_utf8, convert_valid_utf8_to_utf16be),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  588|      1|      ADD(utf16_length_from_utf8, convert_valid_utf8_to_utf16le),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  589|      1|      ADD(utf32_length_from_utf8, convert_valid_utf8_to_utf32),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  590|       |
  591|       |      // all these cases operate on arbitrary data
  592|      1|      ADD(latin1_length_from_utf16, convert_utf16be_to_latin1),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  593|      1|      ADD(utf32_length_from_utf16be, convert_utf16be_to_utf32),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  594|      1|      ADD(utf8_length_from_utf16be, convert_utf16be_to_utf8),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  595|       |
  596|      1|      ADD(latin1_length_from_utf16, convert_utf16le_to_latin1),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  597|      1|      ADD(utf32_length_from_utf16le, convert_utf16le_to_utf32),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  598|      1|      ADD(utf8_length_from_utf16le, convert_utf16le_to_utf8),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  599|       |
  600|      1|      ADD(latin1_length_from_utf32, convert_utf32_to_latin1),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  601|      1|      ADD(utf16_length_from_utf32, convert_utf32_to_utf16be),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  602|      1|      ADD(utf16_length_from_utf32, convert_utf32_to_utf16le),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  603|      1|      ADD(utf8_length_from_utf32, convert_utf32_to_utf8),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  604|       |
  605|      1|      ADD(latin1_length_from_utf8, convert_utf8_to_latin1),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  606|      1|      ADD(utf16_length_from_utf8, convert_utf8_to_utf16be),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  607|      1|      ADD(utf16_length_from_utf8, convert_utf8_to_utf16le),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  608|      1|      ADD(utf32_length_from_utf8, convert_utf8_to_utf32),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  609|       |
  610|       |      // all these cases operate on arbitrary data and use the _with_errors
  611|       |      // variant
  612|      1|      ADD(latin1_length_from_utf16, convert_utf16be_to_latin1_with_errors),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  613|      1|      ADD(utf32_length_from_utf16be, convert_utf16be_to_utf32_with_errors),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  614|      1|      ADD(utf8_length_from_utf16be, convert_utf16be_to_utf8_with_errors),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  615|       |
  616|      1|      ADD(latin1_length_from_utf16, convert_utf16le_to_latin1_with_errors),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  617|      1|      ADD(utf32_length_from_utf16le, convert_utf16le_to_utf32_with_errors),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  618|      1|      ADD(utf8_length_from_utf16le, convert_utf16le_to_utf8_with_errors),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  619|       |
  620|      1|      ADD(latin1_length_from_utf32, convert_utf32_to_latin1_with_errors),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  621|      1|      ADD(utf16_length_from_utf32, convert_utf32_to_utf16be_with_errors),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  622|      1|      ADD(utf16_length_from_utf32, convert_utf32_to_utf16le_with_errors),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  623|      1|      ADD(utf8_length_from_utf32, convert_utf32_to_utf8_with_errors),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  624|       |
  625|      1|      ADD(latin1_length_from_utf8, convert_utf8_to_latin1_with_errors),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  626|      1|      ADD(utf16_length_from_utf8, convert_utf8_to_utf16be_with_errors),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  627|      1|      ADD(utf16_length_from_utf8, convert_utf8_to_utf16le_with_errors),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  628|      1|      ADD(utf32_length_from_utf8, convert_utf8_to_utf32_with_errors),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  629|       |
  630|       |      // these are a bit special since all input is valid
  631|      1|      ADD(utf32_length_from_latin1, convert_latin1_to_utf32),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  632|      1|      ADD(utf16_length_from_latin1, convert_latin1_to_utf16be),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  633|      1|      ADD(utf16_length_from_latin1, convert_latin1_to_utf16le),
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  634|      1|      ADD(utf8_length_from_latin1, convert_latin1_to_utf8)};
  ------------------
  |  |  553|      1|  FuzzSignature {                                                              \
  |  |  554|      1|    +[](std::span<const char> chardata) {                                      \
  |  |  555|      1|      const auto c =                                                           \
  |  |  556|      1|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  |  |  557|      1|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  |  |  558|      1|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  |  |  559|      1|              &I::lenfunc, &I::conversionfunc,                                 \
  |  |  560|      1|              std::string{NAMEOF(&I::lenfunc)},                                \
  |  |  561|      1|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  |  |  562|      1|      c.fuzz(chardata);                                                        \
  |  |  563|      1|    }                                                                          \
  |  |  564|      1|  }
  ------------------
  635|       |
  636|      1|#undef ADD
  637|      1|}
LLVMFuzzerTestOneInput:
  639|  10.0k|extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  640|  10.0k|  static const auto fptrs = populate_functions();
  641|  10.0k|  constexpr std::size_t Ncases = fptrs.size();
  642|       |
  643|       |  // pick one of the function pointers, based on the fuzz data
  644|       |  // the first byte is which action to take. step forward
  645|       |  // several bytes so the input is aligned.
  646|  10.0k|  if (size < 4) {
  ------------------
  |  Branch (646:7): [True: 3, False: 10.0k]
  ------------------
  647|      3|    return 0;
  648|      3|  }
  649|       |
  650|  10.0k|  constexpr auto actionmask = std::bit_ceil(Ncases) - 1;
  651|  10.0k|  const auto action = data[0] & actionmask;
  652|  10.0k|  data += 4;
  653|  10.0k|  size -= 4;
  654|       |
  655|  10.0k|  if (action >= Ncases) {
  ------------------
  |  Branch (655:7): [True: 1, False: 10.0k]
  ------------------
  656|      1|    return 0;
  657|      1|  }
  658|       |
  659|  10.0k|  if constexpr (use_separate_allocation) {
  660|       |    // this is better at exercising null input and catch buffer underflows
  661|  10.0k|    const std::vector<char> separate{data, data + size};
  662|  10.0k|    fptrs[action](std::span(separate));
  663|       |  } else {
  664|       |    std::span<const char> chardata{(const char*)data, size};
  665|       |    fptrs[action](chardata);
  666|       |  }
  667|       |
  668|  10.0k|  return 0;
  669|  10.0k|}
conversion.cpp:_ZZ18populate_functionsvENK3$_0clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    107|    +[](std::span<const char> chardata) {                                      \
  555|    107|      const auto c =                                                           \
  556|    107|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    107|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    107|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    107|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    107|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    107|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    107|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    107|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    107|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    107|  constexpr auto _size = _name.size();                                \
  |  | 1183|    107|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    107|  return _nameof; }()
  ------------------
  561|    107|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    107|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    107|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    107|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    107|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    107|  constexpr auto _size = _name.size();                                \
  |  | 1183|    107|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    107|  return _nameof; }()
  ------------------
  562|    107|      c.fuzz(chardata);                                                        \
  563|    107|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    246|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    246|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    246|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    246|    static const bool do_print_testcase =
  181|    246|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    246|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 246]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    246|    do {
  189|       |      // step 0 - is the input valid?
  190|    246|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    246|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 246]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    246|                    From == UtfEncodings::UTF8) {
  198|    246|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 246]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    246|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    246|      const auto [output_length, length_agree] =
  204|    246|          calculate_length(from, inputisvalid);
  205|    246|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 246]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    246|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 102, False: 144]
  |  Branch (208:28): [True: 17, False: 85]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|     17|        return;
  211|     17|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    229|      const auto [written, outputs_agree] =
  215|    229|          do_conversion(from, output_length, inputisvalid);
  216|    229|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 229]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    229|      return;
  221|    229|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    246|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    246|  validation_result verify_valid_input(FromSpan src) const {
  231|    246|    validation_result ret{};
  232|       |
  233|    246|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    246|    const auto implementations = get_supported_implementations();
  235|    246|    std::vector<simdutf::result> results;
  236|    246|    results.reserve(implementations.size());
  237|       |
  238|    738|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 738, False: 246]
  ------------------
  239|    738|      results.push_back(
  240|    738|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    738|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    738|      const bool validation2 =
  245|    738|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    738|                      src.data(), src.size());
  247|    738|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 738]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|    738|    }
  258|       |
  259|    246|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    246|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 246]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    246|    } else {
  273|    246|      ret.implementations_agree = true;
  274|    246|    }
  275|    246|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    246|      return r.error == simdutf::SUCCESS;
  277|    246|    });
  278|    246|    return ret;
  279|    246|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    492|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKNS1_6resultEE_clESI_:
  275|    534|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    534|      return r.error == simdutf::SUCCESS;
  277|    534|    });
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    246|  bool count_the_input(FromSpan src) const {
  290|    246|    const auto implementations = get_supported_implementations();
  291|    246|    std::vector<std::size_t> results;
  292|    246|    results.reserve(implementations.size());
  293|       |
  294|    738|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 738, False: 246]
  ------------------
  295|    738|      std::size_t ret;
  296|    738|      if constexpr (From == UtfEncodings::UTF16BE) {
  297|    738|        ret = impl->count_utf16be(src.data(), src.size());
  298|       |      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|       |        ret = impl->count_utf16le(src.data(), src.size());
  300|       |      } else if constexpr (From == UtfEncodings::UTF8) {
  301|       |        ret = impl->count_utf8(src.data(), src.size());
  302|       |      }
  303|    738|      results.push_back(ret);
  304|    738|    }
  305|    246|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    246|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 246]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    246|    return true;
  321|    246|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|    492|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    246|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    246|    length_result ret{};
  344|       |
  345|    246|    const auto implementations = get_supported_implementations();
  346|    246|    std::vector<std::size_t> results;
  347|    246|    results.reserve(implementations.size());
  348|       |
  349|    738|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 738, False: 246]
  ------------------
  350|    738|      const auto len = invoke_lengthcalc(impl, src);
  351|    738|      results.push_back(len);
  352|    738|      ret.length.push_back(len);
  353|    738|    }
  354|       |
  355|    246|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    246|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 246]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    246|    } else {
  375|    246|      ret.implementations_agree = true;
  376|    246|    }
  377|    246|    return ret;
  378|    246|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    738|                                FromSpan src) const {
  331|    738|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    738|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    492|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    229|                                  const bool inputisvalid) const {
  383|    229|    conversion_result ret{};
  384|       |
  385|    229|    const auto implementations = get_supported_implementations();
  386|       |
  387|    229|    std::vector<result<ConversionResult>> results;
  388|    229|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    229|    std::vector<std::vector<ToType>> outputbuffers;
  393|    229|    outputbuffers.reserve(implementations.size());
  394|    916|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 687, False: 229]
  ------------------
  395|    687|      auto impl = implementations[i];
  396|    687|      const ToType canary1{42};
  397|    687|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    687|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    687|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    687|      const auto success = [](const ConversionResult& r) -> bool {
  402|    687|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    687|          return r != 0;
  404|    687|        } else {
  405|    687|          return r.error == simdutf::error_code::SUCCESS;
  406|    687|        }
  407|    687|      }(implret1);
  408|    687|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    687|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    687|        const ToType canary2{25};
  414|    687|        const auto outputbuffer_first_run = outputbuffer;
  415|    687|        std::ranges::fill(outputbuffer, canary2);
  416|    687|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    687|                                          src.size(), outputbuffer.data());
  418|       |
  419|    687|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 687]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    687|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 432, False: 255]
  |  Branch (423:29): [True: 420, False: 12]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    420|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    420|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 420]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    420|        }
  440|    687|      }
  441|    687|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 420, False: 267]
  ------------------
  442|    687|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    229|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 85, False: 144]
  ------------------
  447|    255|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 255, False: 85]
  ------------------
  448|    255|        e.outputhash.clear();
  449|    255|      }
  450|     85|    }
  451|       |
  452|    229|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    229|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 229]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    229|    } else {
  474|    229|      ret.implementations_agree = true;
  475|    229|    }
  476|    229|    return ret;
  477|    229|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    687|      const auto success = [](const ConversionResult& r) -> bool {
  402|    687|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    687|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    687|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    458|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_1clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    247|    +[](std::span<const char> chardata) {                                      \
  555|    247|      const auto c =                                                           \
  556|    247|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    247|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    247|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    247|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    247|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    247|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    247|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    247|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    247|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    247|  constexpr auto _size = _name.size();                                \
  |  | 1183|    247|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    247|  return _nameof; }()
  ------------------
  561|    247|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    247|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    247|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    247|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    247|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    247|  constexpr auto _size = _name.size();                                \
  |  | 1183|    247|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    247|  return _nameof; }()
  ------------------
  562|    247|      c.fuzz(chardata);                                                        \
  563|    247|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    445|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    445|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    445|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    445|    static const bool do_print_testcase =
  181|    445|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    445|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 445]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    445|    do {
  189|       |      // step 0 - is the input valid?
  190|    445|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    445|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 445]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    445|                    From == UtfEncodings::UTF8) {
  198|    445|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 445]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    445|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    445|      const auto [output_length, length_agree] =
  204|    445|          calculate_length(from, inputisvalid);
  205|    445|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 445]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    445|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 113, False: 332]
  |  Branch (208:28): [True: 22, False: 91]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|     22|        return;
  211|     22|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    423|      const auto [written, outputs_agree] =
  215|    423|          do_conversion(from, output_length, inputisvalid);
  216|    423|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 423]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    423|      return;
  221|    423|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    445|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    445|  validation_result verify_valid_input(FromSpan src) const {
  231|    445|    validation_result ret{};
  232|       |
  233|    445|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    445|    const auto implementations = get_supported_implementations();
  235|    445|    std::vector<simdutf::result> results;
  236|    445|    results.reserve(implementations.size());
  237|       |
  238|  1.33k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.33k, False: 445]
  ------------------
  239|  1.33k|      results.push_back(
  240|  1.33k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.33k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.33k|      const bool validation2 =
  245|  1.33k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.33k|                      src.data(), src.size());
  247|  1.33k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.33k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  1.33k|    }
  258|       |
  259|    445|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    445|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 445]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    445|    } else {
  273|    445|      ret.implementations_agree = true;
  274|    445|    }
  275|    445|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    445|      return r.error == simdutf::SUCCESS;
  277|    445|    });
  278|    445|    return ret;
  279|    445|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    890|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKNS1_6resultEE_clESI_:
  275|  1.10k|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|  1.10k|      return r.error == simdutf::SUCCESS;
  277|  1.10k|    });
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    445|  bool count_the_input(FromSpan src) const {
  290|    445|    const auto implementations = get_supported_implementations();
  291|    445|    std::vector<std::size_t> results;
  292|    445|    results.reserve(implementations.size());
  293|       |
  294|  1.33k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.33k, False: 445]
  ------------------
  295|  1.33k|      std::size_t ret;
  296|  1.33k|      if constexpr (From == UtfEncodings::UTF16BE) {
  297|  1.33k|        ret = impl->count_utf16be(src.data(), src.size());
  298|       |      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|       |        ret = impl->count_utf16le(src.data(), src.size());
  300|       |      } else if constexpr (From == UtfEncodings::UTF8) {
  301|       |        ret = impl->count_utf8(src.data(), src.size());
  302|       |      }
  303|  1.33k|      results.push_back(ret);
  304|  1.33k|    }
  305|    445|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    445|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 445]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    445|    return true;
  321|    445|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|    890|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    445|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    445|    length_result ret{};
  344|       |
  345|    445|    const auto implementations = get_supported_implementations();
  346|    445|    std::vector<std::size_t> results;
  347|    445|    results.reserve(implementations.size());
  348|       |
  349|  1.33k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.33k, False: 445]
  ------------------
  350|  1.33k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.33k|      results.push_back(len);
  352|  1.33k|      ret.length.push_back(len);
  353|  1.33k|    }
  354|       |
  355|    445|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    445|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 445]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    445|    } else {
  375|    445|      ret.implementations_agree = true;
  376|    445|    }
  377|    445|    return ret;
  378|    445|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.33k|                                FromSpan src) const {
  331|  1.33k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.33k|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    890|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    423|                                  const bool inputisvalid) const {
  383|    423|    conversion_result ret{};
  384|       |
  385|    423|    const auto implementations = get_supported_implementations();
  386|       |
  387|    423|    std::vector<result<ConversionResult>> results;
  388|    423|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    423|    std::vector<std::vector<ToType>> outputbuffers;
  393|    423|    outputbuffers.reserve(implementations.size());
  394|  1.69k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.26k, False: 423]
  ------------------
  395|  1.26k|      auto impl = implementations[i];
  396|  1.26k|      const ToType canary1{42};
  397|  1.26k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.26k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.26k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.26k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.26k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.26k|          return r != 0;
  404|  1.26k|        } else {
  405|  1.26k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.26k|        }
  407|  1.26k|      }(implret1);
  408|  1.26k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.26k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  1.26k|        const ToType canary2{25};
  414|  1.26k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.26k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.26k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.26k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.26k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.26k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.26k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 996, False: 273]
  |  Branch (423:29): [True: 987, False: 9]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    987|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    987|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 987]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    987|        }
  440|  1.26k|      }
  441|  1.26k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 987, False: 282]
  ------------------
  442|  1.26k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    423|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 91, False: 332]
  ------------------
  447|    273|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 273, False: 91]
  ------------------
  448|    273|        e.outputhash.clear();
  449|    273|      }
  450|     91|    }
  451|       |
  452|    423|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    423|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 423]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    423|    } else {
  474|    423|      ret.implementations_agree = true;
  475|    423|    }
  476|    423|    return ret;
  477|    423|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|  1.26k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.26k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.26k|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  1.26k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    846|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_2clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     70|    +[](std::span<const char> chardata) {                                      \
  555|     70|      const auto c =                                                           \
  556|     70|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     70|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     70|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     70|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     70|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     70|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     70|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     70|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     70|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     70|  constexpr auto _size = _name.size();                                \
  |  | 1183|     70|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     70|  return _nameof; }()
  ------------------
  561|     70|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     70|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     70|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     70|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     70|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     70|  constexpr auto _size = _name.size();                                \
  |  | 1183|     70|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     70|  return _nameof; }()
  ------------------
  562|     70|      c.fuzz(chardata);                                                        \
  563|     70|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    221|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    221|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    221|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    221|    static const bool do_print_testcase =
  181|    221|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    221|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 221]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    221|    do {
  189|       |      // step 0 - is the input valid?
  190|    221|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    221|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 221]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    221|                    From == UtfEncodings::UTF8) {
  198|    221|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 221]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    221|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    221|      const auto [output_length, length_agree] =
  204|    221|          calculate_length(from, inputisvalid);
  205|    221|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 221]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    221|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 97, False: 124]
  |  Branch (208:28): [True: 6, False: 91]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      6|        return;
  211|      6|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    215|      const auto [written, outputs_agree] =
  215|    215|          do_conversion(from, output_length, inputisvalid);
  216|    215|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 215]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    215|      return;
  221|    215|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    221|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    221|  validation_result verify_valid_input(FromSpan src) const {
  231|    221|    validation_result ret{};
  232|       |
  233|    221|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    221|    const auto implementations = get_supported_implementations();
  235|    221|    std::vector<simdutf::result> results;
  236|    221|    results.reserve(implementations.size());
  237|       |
  238|    663|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 663, False: 221]
  ------------------
  239|    663|      results.push_back(
  240|    663|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    663|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    663|      const bool validation2 =
  245|    663|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    663|                      src.data(), src.size());
  247|    663|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 663]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|    663|    }
  258|       |
  259|    221|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    221|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 221]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    221|    } else {
  273|    221|      ret.implementations_agree = true;
  274|    221|    }
  275|    221|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    221|      return r.error == simdutf::SUCCESS;
  277|    221|    });
  278|    221|    return ret;
  279|    221|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    442|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKNS1_6resultEE_clESI_:
  275|    469|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    469|      return r.error == simdutf::SUCCESS;
  277|    469|    });
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    221|  bool count_the_input(FromSpan src) const {
  290|    221|    const auto implementations = get_supported_implementations();
  291|    221|    std::vector<std::size_t> results;
  292|    221|    results.reserve(implementations.size());
  293|       |
  294|    663|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 663, False: 221]
  ------------------
  295|    663|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|    663|      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|    663|        ret = impl->count_utf16le(src.data(), src.size());
  300|       |      } else if constexpr (From == UtfEncodings::UTF8) {
  301|       |        ret = impl->count_utf8(src.data(), src.size());
  302|       |      }
  303|    663|      results.push_back(ret);
  304|    663|    }
  305|    221|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    221|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 221]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    221|    return true;
  321|    221|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|    442|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    221|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    221|    length_result ret{};
  344|       |
  345|    221|    const auto implementations = get_supported_implementations();
  346|    221|    std::vector<std::size_t> results;
  347|    221|    results.reserve(implementations.size());
  348|       |
  349|    663|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 663, False: 221]
  ------------------
  350|    663|      const auto len = invoke_lengthcalc(impl, src);
  351|    663|      results.push_back(len);
  352|    663|      ret.length.push_back(len);
  353|    663|    }
  354|       |
  355|    221|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    221|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 221]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    221|    } else {
  375|    221|      ret.implementations_agree = true;
  376|    221|    }
  377|    221|    return ret;
  378|    221|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    663|                                FromSpan src) const {
  331|    663|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    663|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    442|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    215|                                  const bool inputisvalid) const {
  383|    215|    conversion_result ret{};
  384|       |
  385|    215|    const auto implementations = get_supported_implementations();
  386|       |
  387|    215|    std::vector<result<ConversionResult>> results;
  388|    215|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    215|    std::vector<std::vector<ToType>> outputbuffers;
  393|    215|    outputbuffers.reserve(implementations.size());
  394|    860|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 645, False: 215]
  ------------------
  395|    645|      auto impl = implementations[i];
  396|    645|      const ToType canary1{42};
  397|    645|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    645|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    645|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    645|      const auto success = [](const ConversionResult& r) -> bool {
  402|    645|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    645|          return r != 0;
  404|    645|        } else {
  405|    645|          return r.error == simdutf::error_code::SUCCESS;
  406|    645|        }
  407|    645|      }(implret1);
  408|    645|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    645|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    645|        const ToType canary2{25};
  414|    645|        const auto outputbuffer_first_run = outputbuffer;
  415|    645|        std::ranges::fill(outputbuffer, canary2);
  416|    645|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    645|                                          src.size(), outputbuffer.data());
  418|       |
  419|    645|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 645]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    645|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 372, False: 273]
  |  Branch (423:29): [True: 360, False: 12]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    360|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    360|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 360]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    360|        }
  440|    645|      }
  441|    645|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 360, False: 285]
  ------------------
  442|    645|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    215|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 91, False: 124]
  ------------------
  447|    273|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 273, False: 91]
  ------------------
  448|    273|        e.outputhash.clear();
  449|    273|      }
  450|     91|    }
  451|       |
  452|    215|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    215|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 215]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    215|    } else {
  474|    215|      ret.implementations_agree = true;
  475|    215|    }
  476|    215|    return ret;
  477|    215|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    645|      const auto success = [](const ConversionResult& r) -> bool {
  402|    645|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    645|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    645|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    430|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_3clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    214|    +[](std::span<const char> chardata) {                                      \
  555|    214|      const auto c =                                                           \
  556|    214|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    214|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    214|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    214|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    214|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    214|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    214|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    214|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    214|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    214|  constexpr auto _size = _name.size();                                \
  |  | 1183|    214|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    214|  return _nameof; }()
  ------------------
  561|    214|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    214|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    214|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    214|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    214|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    214|  constexpr auto _size = _name.size();                                \
  |  | 1183|    214|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    214|  return _nameof; }()
  ------------------
  562|    214|      c.fuzz(chardata);                                                        \
  563|    214|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    450|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    450|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    450|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    450|    static const bool do_print_testcase =
  181|    450|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    450|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 450]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    450|    do {
  189|       |      // step 0 - is the input valid?
  190|    450|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    450|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 450]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    450|                    From == UtfEncodings::UTF8) {
  198|    450|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 450]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    450|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    450|      const auto [output_length, length_agree] =
  204|    450|          calculate_length(from, inputisvalid);
  205|    450|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 450]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    450|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 107, False: 343]
  |  Branch (208:28): [True: 13, False: 94]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|     13|        return;
  211|     13|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    437|      const auto [written, outputs_agree] =
  215|    437|          do_conversion(from, output_length, inputisvalid);
  216|    437|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 437]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    437|      return;
  221|    437|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    450|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    450|  validation_result verify_valid_input(FromSpan src) const {
  231|    450|    validation_result ret{};
  232|       |
  233|    450|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    450|    const auto implementations = get_supported_implementations();
  235|    450|    std::vector<simdutf::result> results;
  236|    450|    results.reserve(implementations.size());
  237|       |
  238|  1.35k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.35k, False: 450]
  ------------------
  239|  1.35k|      results.push_back(
  240|  1.35k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.35k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.35k|      const bool validation2 =
  245|  1.35k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.35k|                      src.data(), src.size());
  247|  1.35k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.35k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  1.35k|    }
  258|       |
  259|    450|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    450|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 450]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    450|    } else {
  273|    450|      ret.implementations_agree = true;
  274|    450|    }
  275|    450|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    450|      return r.error == simdutf::SUCCESS;
  277|    450|    });
  278|    450|    return ret;
  279|    450|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    900|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKNS1_6resultEE_clESI_:
  275|  1.13k|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|  1.13k|      return r.error == simdutf::SUCCESS;
  277|  1.13k|    });
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    450|  bool count_the_input(FromSpan src) const {
  290|    450|    const auto implementations = get_supported_implementations();
  291|    450|    std::vector<std::size_t> results;
  292|    450|    results.reserve(implementations.size());
  293|       |
  294|  1.35k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.35k, False: 450]
  ------------------
  295|  1.35k|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|  1.35k|      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|  1.35k|        ret = impl->count_utf16le(src.data(), src.size());
  300|       |      } else if constexpr (From == UtfEncodings::UTF8) {
  301|       |        ret = impl->count_utf8(src.data(), src.size());
  302|       |      }
  303|  1.35k|      results.push_back(ret);
  304|  1.35k|    }
  305|    450|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    450|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 450]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    450|    return true;
  321|    450|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|    900|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    450|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    450|    length_result ret{};
  344|       |
  345|    450|    const auto implementations = get_supported_implementations();
  346|    450|    std::vector<std::size_t> results;
  347|    450|    results.reserve(implementations.size());
  348|       |
  349|  1.35k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.35k, False: 450]
  ------------------
  350|  1.35k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.35k|      results.push_back(len);
  352|  1.35k|      ret.length.push_back(len);
  353|  1.35k|    }
  354|       |
  355|    450|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    450|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 450]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    450|    } else {
  375|    450|      ret.implementations_agree = true;
  376|    450|    }
  377|    450|    return ret;
  378|    450|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.35k|                                FromSpan src) const {
  331|  1.35k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.35k|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    900|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    437|                                  const bool inputisvalid) const {
  383|    437|    conversion_result ret{};
  384|       |
  385|    437|    const auto implementations = get_supported_implementations();
  386|       |
  387|    437|    std::vector<result<ConversionResult>> results;
  388|    437|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    437|    std::vector<std::vector<ToType>> outputbuffers;
  393|    437|    outputbuffers.reserve(implementations.size());
  394|  1.74k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.31k, False: 437]
  ------------------
  395|  1.31k|      auto impl = implementations[i];
  396|  1.31k|      const ToType canary1{42};
  397|  1.31k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.31k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.31k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.31k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.31k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.31k|          return r != 0;
  404|  1.31k|        } else {
  405|  1.31k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.31k|        }
  407|  1.31k|      }(implret1);
  408|  1.31k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.31k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  1.31k|        const ToType canary2{25};
  414|  1.31k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.31k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.31k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.31k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.31k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.31k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.31k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 1.02k, False: 282]
  |  Branch (423:29): [True: 1.01k, False: 12]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|  1.01k|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|  1.01k|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 1.01k]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|  1.01k|        }
  440|  1.31k|      }
  441|  1.31k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 1.01k, False: 294]
  ------------------
  442|  1.31k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    437|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 94, False: 343]
  ------------------
  447|    282|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 282, False: 94]
  ------------------
  448|    282|        e.outputhash.clear();
  449|    282|      }
  450|     94|    }
  451|       |
  452|    437|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    437|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 437]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    437|    } else {
  474|    437|      ret.implementations_agree = true;
  475|    437|    }
  476|    437|    return ret;
  477|    437|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|  1.31k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.31k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.31k|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  1.31k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    874|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_4clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     71|    +[](std::span<const char> chardata) {                                      \
  555|     71|      const auto c =                                                           \
  556|     71|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     71|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     71|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     71|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     71|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     71|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     71|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     71|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     71|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     71|  constexpr auto _size = _name.size();                                \
  |  | 1183|     71|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     71|  return _nameof; }()
  ------------------
  561|     71|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     71|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     71|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     71|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     71|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     71|  constexpr auto _size = _name.size();                                \
  |  | 1183|     71|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     71|  return _nameof; }()
  ------------------
  562|     71|      c.fuzz(chardata);                                                        \
  563|     71|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    315|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    315|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    315|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    315|    static const bool do_print_testcase =
  181|    315|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    315|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 315]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    315|    do {
  189|       |      // step 0 - is the input valid?
  190|    315|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    315|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 315]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|       |                    From == UtfEncodings::UTF8) {
  198|       |        if (!count_the_input(from) && !allow_implementations_to_differ)
  199|       |          break;
  200|       |      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    315|      const auto [output_length, length_agree] =
  204|    315|          calculate_length(from, inputisvalid);
  205|    315|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 315]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    315|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 174, False: 141]
  |  Branch (208:28): [True: 5, False: 169]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      5|        return;
  211|      5|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    310|      const auto [written, outputs_agree] =
  215|    310|          do_conversion(from, output_length, inputisvalid);
  216|    310|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 310]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    310|      return;
  221|    310|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    315|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    315|  validation_result verify_valid_input(FromSpan src) const {
  231|    315|    validation_result ret{};
  232|       |
  233|    315|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    315|    const auto implementations = get_supported_implementations();
  235|    315|    std::vector<simdutf::result> results;
  236|    315|    results.reserve(implementations.size());
  237|       |
  238|    945|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 945, False: 315]
  ------------------
  239|    945|      results.push_back(
  240|    945|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    945|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    945|      const bool validation2 =
  245|    945|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    945|                      src.data(), src.size());
  247|    945|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 945]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|    945|    }
  258|       |
  259|    315|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    315|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 315]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    315|    } else {
  273|    315|      ret.implementations_agree = true;
  274|    315|    }
  275|    315|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    315|      return r.error == simdutf::SUCCESS;
  277|    315|    });
  278|    315|    return ret;
  279|    315|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    630|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKNS1_6resultEE_clESI_:
  275|    597|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    597|      return r.error == simdutf::SUCCESS;
  277|    597|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    315|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    315|    length_result ret{};
  344|       |
  345|    315|    const auto implementations = get_supported_implementations();
  346|    315|    std::vector<std::size_t> results;
  347|    315|    results.reserve(implementations.size());
  348|       |
  349|    945|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 945, False: 315]
  ------------------
  350|    945|      const auto len = invoke_lengthcalc(impl, src);
  351|    945|      results.push_back(len);
  352|    945|      ret.length.push_back(len);
  353|    945|    }
  354|       |
  355|    315|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    315|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 315]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    315|    } else {
  375|    315|      ret.implementations_agree = true;
  376|    315|    }
  377|    315|    return ret;
  378|    315|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    945|                                FromSpan src) const {
  331|    945|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    945|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    630|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    310|                                  const bool inputisvalid) const {
  383|    310|    conversion_result ret{};
  384|       |
  385|    310|    const auto implementations = get_supported_implementations();
  386|       |
  387|    310|    std::vector<result<ConversionResult>> results;
  388|    310|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    310|    std::vector<std::vector<ToType>> outputbuffers;
  393|    310|    outputbuffers.reserve(implementations.size());
  394|  1.24k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 930, False: 310]
  ------------------
  395|    930|      auto impl = implementations[i];
  396|    930|      const ToType canary1{42};
  397|    930|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    930|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    930|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    930|      const auto success = [](const ConversionResult& r) -> bool {
  402|    930|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    930|          return r != 0;
  404|    930|        } else {
  405|    930|          return r.error == simdutf::error_code::SUCCESS;
  406|    930|        }
  407|    930|      }(implret1);
  408|    930|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    930|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    930|        const ToType canary2{25};
  414|    930|        const auto outputbuffer_first_run = outputbuffer;
  415|    930|        std::ranges::fill(outputbuffer, canary2);
  416|    930|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    930|                                          src.size(), outputbuffer.data());
  418|       |
  419|    930|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 930]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    930|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 423, False: 507]
  |  Branch (423:29): [True: 414, False: 9]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    414|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    414|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 414]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    414|        }
  440|    930|      }
  441|    930|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 414, False: 516]
  ------------------
  442|    930|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    310|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 169, False: 141]
  ------------------
  447|    507|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 507, False: 169]
  ------------------
  448|    507|        e.outputhash.clear();
  449|    507|      }
  450|    169|    }
  451|       |
  452|    310|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    310|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 310]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    310|    } else {
  474|    310|      ret.implementations_agree = true;
  475|    310|    }
  476|    310|    return ret;
  477|    310|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    930|      const auto success = [](const ConversionResult& r) -> bool {
  402|    930|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    930|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    930|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    620|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_5clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    165|    +[](std::span<const char> chardata) {                                      \
  555|    165|      const auto c =                                                           \
  556|    165|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    165|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    165|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    165|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    165|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    165|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    165|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    165|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    165|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    165|  constexpr auto _size = _name.size();                                \
  |  | 1183|    165|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    165|  return _nameof; }()
  ------------------
  561|    165|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    165|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    165|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    165|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    165|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    165|  constexpr auto _size = _name.size();                                \
  |  | 1183|    165|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    165|  return _nameof; }()
  ------------------
  562|    165|      c.fuzz(chardata);                                                        \
  563|    165|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    408|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    408|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    408|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    408|    static const bool do_print_testcase =
  181|    408|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    408|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 408]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    408|    do {
  189|       |      // step 0 - is the input valid?
  190|    408|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    408|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 408]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|       |                    From == UtfEncodings::UTF8) {
  198|       |        if (!count_the_input(from) && !allow_implementations_to_differ)
  199|       |          break;
  200|       |      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    408|      const auto [output_length, length_agree] =
  204|    408|          calculate_length(from, inputisvalid);
  205|    408|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 408]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    408|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 185, False: 223]
  |  Branch (208:28): [True: 5, False: 180]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      5|        return;
  211|      5|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    403|      const auto [written, outputs_agree] =
  215|    403|          do_conversion(from, output_length, inputisvalid);
  216|    403|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 403]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    403|      return;
  221|    403|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    408|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    408|  validation_result verify_valid_input(FromSpan src) const {
  231|    408|    validation_result ret{};
  232|       |
  233|    408|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    408|    const auto implementations = get_supported_implementations();
  235|    408|    std::vector<simdutf::result> results;
  236|    408|    results.reserve(implementations.size());
  237|       |
  238|  1.22k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.22k, False: 408]
  ------------------
  239|  1.22k|      results.push_back(
  240|  1.22k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.22k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.22k|      const bool validation2 =
  245|  1.22k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.22k|                      src.data(), src.size());
  247|  1.22k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.22k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  1.22k|    }
  258|       |
  259|    408|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    408|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 408]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    408|    } else {
  273|    408|      ret.implementations_agree = true;
  274|    408|    }
  275|    408|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    408|      return r.error == simdutf::SUCCESS;
  277|    408|    });
  278|    408|    return ret;
  279|    408|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    816|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKNS1_6resultEE_clESI_:
  275|    854|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    854|      return r.error == simdutf::SUCCESS;
  277|    854|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    408|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    408|    length_result ret{};
  344|       |
  345|    408|    const auto implementations = get_supported_implementations();
  346|    408|    std::vector<std::size_t> results;
  347|    408|    results.reserve(implementations.size());
  348|       |
  349|  1.22k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.22k, False: 408]
  ------------------
  350|  1.22k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.22k|      results.push_back(len);
  352|  1.22k|      ret.length.push_back(len);
  353|  1.22k|    }
  354|       |
  355|    408|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    408|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 408]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    408|    } else {
  375|    408|      ret.implementations_agree = true;
  376|    408|    }
  377|    408|    return ret;
  378|    408|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.22k|                                FromSpan src) const {
  331|  1.22k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.22k|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    816|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    403|                                  const bool inputisvalid) const {
  383|    403|    conversion_result ret{};
  384|       |
  385|    403|    const auto implementations = get_supported_implementations();
  386|       |
  387|    403|    std::vector<result<ConversionResult>> results;
  388|    403|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    403|    std::vector<std::vector<ToType>> outputbuffers;
  393|    403|    outputbuffers.reserve(implementations.size());
  394|  1.61k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.20k, False: 403]
  ------------------
  395|  1.20k|      auto impl = implementations[i];
  396|  1.20k|      const ToType canary1{42};
  397|  1.20k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.20k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.20k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.20k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.20k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.20k|          return r != 0;
  404|  1.20k|        } else {
  405|  1.20k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.20k|        }
  407|  1.20k|      }(implret1);
  408|  1.20k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.20k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  1.20k|        const ToType canary2{25};
  414|  1.20k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.20k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.20k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.20k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.20k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.20k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.20k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 669, False: 540]
  |  Branch (423:29): [True: 654, False: 15]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    654|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    654|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 654]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    654|        }
  440|  1.20k|      }
  441|  1.20k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 654, False: 555]
  ------------------
  442|  1.20k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    403|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 180, False: 223]
  ------------------
  447|    540|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 540, False: 180]
  ------------------
  448|    540|        e.outputhash.clear();
  449|    540|      }
  450|    180|    }
  451|       |
  452|    403|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    403|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 403]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    403|    } else {
  474|    403|      ret.implementations_agree = true;
  475|    403|    }
  476|    403|    return ret;
  477|    403|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|  1.20k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.20k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.20k|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  1.20k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    806|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_6clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    118|    +[](std::span<const char> chardata) {                                      \
  555|    118|      const auto c =                                                           \
  556|    118|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    118|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    118|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    118|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    118|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    118|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    118|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    118|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    118|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    118|  constexpr auto _size = _name.size();                                \
  |  | 1183|    118|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    118|  return _nameof; }()
  ------------------
  561|    118|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    118|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    118|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    118|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    118|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    118|  constexpr auto _size = _name.size();                                \
  |  | 1183|    118|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    118|  return _nameof; }()
  ------------------
  562|    118|      c.fuzz(chardata);                                                        \
  563|    118|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    481|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    481|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    481|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    481|    static const bool do_print_testcase =
  181|    481|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    481|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 481]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    481|    do {
  189|       |      // step 0 - is the input valid?
  190|    481|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    481|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 481]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|       |                    From == UtfEncodings::UTF8) {
  198|       |        if (!count_the_input(from) && !allow_implementations_to_differ)
  199|       |          break;
  200|       |      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    481|      const auto [output_length, length_agree] =
  204|    481|          calculate_length(from, inputisvalid);
  205|    481|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 481]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    481|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 304, False: 177]
  |  Branch (208:28): [True: 10, False: 294]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|     10|        return;
  211|     10|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    471|      const auto [written, outputs_agree] =
  215|    471|          do_conversion(from, output_length, inputisvalid);
  216|    471|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 471]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    471|      return;
  221|    471|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    481|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    481|  validation_result verify_valid_input(FromSpan src) const {
  231|    481|    validation_result ret{};
  232|       |
  233|    481|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    481|    const auto implementations = get_supported_implementations();
  235|    481|    std::vector<simdutf::result> results;
  236|    481|    results.reserve(implementations.size());
  237|       |
  238|  1.44k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.44k, False: 481]
  ------------------
  239|  1.44k|      results.push_back(
  240|  1.44k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.44k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.44k|      const bool validation2 =
  245|  1.44k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.44k|                      src.data(), src.size());
  247|  1.44k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.44k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  1.44k|    }
  258|       |
  259|    481|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    481|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 481]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    481|    } else {
  273|    481|      ret.implementations_agree = true;
  274|    481|    }
  275|    481|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    481|      return r.error == simdutf::SUCCESS;
  277|    481|    });
  278|    481|    return ret;
  279|    481|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    962|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKNS1_6resultEE_clESI_:
  275|    835|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    835|      return r.error == simdutf::SUCCESS;
  277|    835|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    481|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    481|    length_result ret{};
  344|       |
  345|    481|    const auto implementations = get_supported_implementations();
  346|    481|    std::vector<std::size_t> results;
  347|    481|    results.reserve(implementations.size());
  348|       |
  349|  1.44k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.44k, False: 481]
  ------------------
  350|  1.44k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.44k|      results.push_back(len);
  352|  1.44k|      ret.length.push_back(len);
  353|  1.44k|    }
  354|       |
  355|    481|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    481|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 481]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    481|    } else {
  375|    481|      ret.implementations_agree = true;
  376|    481|    }
  377|    481|    return ret;
  378|    481|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.44k|                                FromSpan src) const {
  331|  1.44k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.44k|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    962|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    471|                                  const bool inputisvalid) const {
  383|    471|    conversion_result ret{};
  384|       |
  385|    471|    const auto implementations = get_supported_implementations();
  386|       |
  387|    471|    std::vector<result<ConversionResult>> results;
  388|    471|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    471|    std::vector<std::vector<ToType>> outputbuffers;
  393|    471|    outputbuffers.reserve(implementations.size());
  394|  1.88k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.41k, False: 471]
  ------------------
  395|  1.41k|      auto impl = implementations[i];
  396|  1.41k|      const ToType canary1{42};
  397|  1.41k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.41k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.41k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.41k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.41k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.41k|          return r != 0;
  404|  1.41k|        } else {
  405|  1.41k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.41k|        }
  407|  1.41k|      }(implret1);
  408|  1.41k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.41k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  1.41k|        const ToType canary2{25};
  414|  1.41k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.41k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.41k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.41k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.41k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.41k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.41k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 531, False: 882]
  |  Branch (423:29): [True: 519, False: 12]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    519|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    519|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 519]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    519|        }
  440|  1.41k|      }
  441|  1.41k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 519, False: 894]
  ------------------
  442|  1.41k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    471|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 294, False: 177]
  ------------------
  447|    882|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 882, False: 294]
  ------------------
  448|    882|        e.outputhash.clear();
  449|    882|      }
  450|    294|    }
  451|       |
  452|    471|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    471|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 471]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    471|    } else {
  474|    471|      ret.implementations_agree = true;
  475|    471|    }
  476|    471|    return ret;
  477|    471|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|  1.41k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.41k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.41k|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  1.41k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    942|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_7clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    264|    +[](std::span<const char> chardata) {                                      \
  555|    264|      const auto c =                                                           \
  556|    264|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    264|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    264|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    264|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    264|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    264|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    264|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    264|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    264|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    264|  constexpr auto _size = _name.size();                                \
  |  | 1183|    264|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    264|  return _nameof; }()
  ------------------
  561|    264|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    264|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    264|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    264|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    264|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    264|  constexpr auto _size = _name.size();                                \
  |  | 1183|    264|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    264|  return _nameof; }()
  ------------------
  562|    264|      c.fuzz(chardata);                                                        \
  563|    264|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    768|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    768|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    768|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    768|    static const bool do_print_testcase =
  181|    768|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    768|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 768]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    768|    do {
  189|       |      // step 0 - is the input valid?
  190|    768|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    768|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 768]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    768|                    From == UtfEncodings::UTF8) {
  198|    768|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 768]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    768|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    768|      const auto [output_length, length_agree] =
  204|    768|          calculate_length(from, inputisvalid);
  205|    768|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 768]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    768|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 408, False: 360]
  |  Branch (208:28): [True: 5, False: 403]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      5|        return;
  211|      5|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    763|      const auto [written, outputs_agree] =
  215|    763|          do_conversion(from, output_length, inputisvalid);
  216|    763|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 763]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    763|      return;
  221|    763|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    768|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    768|  validation_result verify_valid_input(FromSpan src) const {
  231|    768|    validation_result ret{};
  232|       |
  233|    768|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    768|    const auto implementations = get_supported_implementations();
  235|    768|    std::vector<simdutf::result> results;
  236|    768|    results.reserve(implementations.size());
  237|       |
  238|  2.30k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 2.30k, False: 768]
  ------------------
  239|  2.30k|      results.push_back(
  240|  2.30k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  2.30k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  2.30k|      const bool validation2 =
  245|  2.30k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  2.30k|                      src.data(), src.size());
  247|  2.30k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 2.30k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  2.30k|    }
  258|       |
  259|    768|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    768|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 768]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    768|    } else {
  273|    768|      ret.implementations_agree = true;
  274|    768|    }
  275|    768|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    768|      return r.error == simdutf::SUCCESS;
  277|    768|    });
  278|    768|    return ret;
  279|    768|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|  1.53k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKNS1_6resultEE_clESI_:
  275|  1.48k|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|  1.48k|      return r.error == simdutf::SUCCESS;
  277|  1.48k|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    768|  bool count_the_input(FromSpan src) const {
  290|    768|    const auto implementations = get_supported_implementations();
  291|    768|    std::vector<std::size_t> results;
  292|    768|    results.reserve(implementations.size());
  293|       |
  294|  2.30k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 2.30k, False: 768]
  ------------------
  295|  2.30k|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|       |      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|       |        ret = impl->count_utf16le(src.data(), src.size());
  300|  2.30k|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|  2.30k|        ret = impl->count_utf8(src.data(), src.size());
  302|  2.30k|      }
  303|  2.30k|      results.push_back(ret);
  304|  2.30k|    }
  305|    768|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    768|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 768]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    768|    return true;
  321|    768|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|  1.53k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    768|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    768|    length_result ret{};
  344|       |
  345|    768|    const auto implementations = get_supported_implementations();
  346|    768|    std::vector<std::size_t> results;
  347|    768|    results.reserve(implementations.size());
  348|       |
  349|  2.30k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 2.30k, False: 768]
  ------------------
  350|  2.30k|      const auto len = invoke_lengthcalc(impl, src);
  351|  2.30k|      results.push_back(len);
  352|  2.30k|      ret.length.push_back(len);
  353|  2.30k|    }
  354|       |
  355|    768|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    768|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 768]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    768|    } else {
  375|    768|      ret.implementations_agree = true;
  376|    768|    }
  377|    768|    return ret;
  378|    768|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  2.30k|                                FromSpan src) const {
  331|  2.30k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  2.30k|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|  1.53k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    763|                                  const bool inputisvalid) const {
  383|    763|    conversion_result ret{};
  384|       |
  385|    763|    const auto implementations = get_supported_implementations();
  386|       |
  387|    763|    std::vector<result<ConversionResult>> results;
  388|    763|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    763|    std::vector<std::vector<ToType>> outputbuffers;
  393|    763|    outputbuffers.reserve(implementations.size());
  394|  3.05k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 2.28k, False: 763]
  ------------------
  395|  2.28k|      auto impl = implementations[i];
  396|  2.28k|      const ToType canary1{42};
  397|  2.28k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  2.28k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  2.28k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  2.28k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  2.28k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  2.28k|          return r != 0;
  404|  2.28k|        } else {
  405|  2.28k|          return r.error == simdutf::error_code::SUCCESS;
  406|  2.28k|        }
  407|  2.28k|      }(implret1);
  408|  2.28k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  2.28k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  2.28k|        const ToType canary2{25};
  414|  2.28k|        const auto outputbuffer_first_run = outputbuffer;
  415|  2.28k|        std::ranges::fill(outputbuffer, canary2);
  416|  2.28k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  2.28k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  2.28k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 2.28k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  2.28k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 1.08k, False: 1.20k]
  |  Branch (423:29): [True: 1.07k, False: 6]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|  1.07k|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|  1.07k|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 1.07k]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|  1.07k|        }
  440|  2.28k|      }
  441|  2.28k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 1.07k, False: 1.21k]
  ------------------
  442|  2.28k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    763|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 403, False: 360]
  ------------------
  447|  1.20k|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 1.20k, False: 403]
  ------------------
  448|  1.20k|        e.outputhash.clear();
  449|  1.20k|      }
  450|    403|    }
  451|       |
  452|    763|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    763|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 763]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    763|    } else {
  474|    763|      ret.implementations_agree = true;
  475|    763|    }
  476|    763|    return ret;
  477|    763|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|  2.28k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  2.28k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  2.28k|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  2.28k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|  1.52k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_8clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    206|    +[](std::span<const char> chardata) {                                      \
  555|    206|      const auto c =                                                           \
  556|    206|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    206|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    206|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    206|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    206|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    206|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    206|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    206|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    206|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    206|  constexpr auto _size = _name.size();                                \
  |  | 1183|    206|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    206|  return _nameof; }()
  ------------------
  561|    206|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    206|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    206|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    206|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    206|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    206|  constexpr auto _size = _name.size();                                \
  |  | 1183|    206|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    206|  return _nameof; }()
  ------------------
  562|    206|      c.fuzz(chardata);                                                        \
  563|    206|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    681|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    681|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    681|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    681|    static const bool do_print_testcase =
  181|    681|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    681|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 681]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    681|    do {
  189|       |      // step 0 - is the input valid?
  190|    681|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    681|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 681]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    681|                    From == UtfEncodings::UTF8) {
  198|    681|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 681]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    681|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    681|      const auto [output_length, length_agree] =
  204|    681|          calculate_length(from, inputisvalid);
  205|    681|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 681]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    681|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 365, False: 316]
  |  Branch (208:28): [True: 16, False: 349]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|     16|        return;
  211|     16|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    665|      const auto [written, outputs_agree] =
  215|    665|          do_conversion(from, output_length, inputisvalid);
  216|    665|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 665]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    665|      return;
  221|    665|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    681|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    681|  validation_result verify_valid_input(FromSpan src) const {
  231|    681|    validation_result ret{};
  232|       |
  233|    681|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    681|    const auto implementations = get_supported_implementations();
  235|    681|    std::vector<simdutf::result> results;
  236|    681|    results.reserve(implementations.size());
  237|       |
  238|  2.04k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 2.04k, False: 681]
  ------------------
  239|  2.04k|      results.push_back(
  240|  2.04k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  2.04k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  2.04k|      const bool validation2 =
  245|  2.04k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  2.04k|                      src.data(), src.size());
  247|  2.04k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 2.04k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  2.04k|    }
  258|       |
  259|    681|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    681|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 681]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    681|    } else {
  273|    681|      ret.implementations_agree = true;
  274|    681|    }
  275|    681|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    681|      return r.error == simdutf::SUCCESS;
  277|    681|    });
  278|    681|    return ret;
  279|    681|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|  1.36k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKNS1_6resultEE_clESI_:
  275|  1.31k|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|  1.31k|      return r.error == simdutf::SUCCESS;
  277|  1.31k|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    681|  bool count_the_input(FromSpan src) const {
  290|    681|    const auto implementations = get_supported_implementations();
  291|    681|    std::vector<std::size_t> results;
  292|    681|    results.reserve(implementations.size());
  293|       |
  294|  2.04k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 2.04k, False: 681]
  ------------------
  295|  2.04k|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|       |      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|       |        ret = impl->count_utf16le(src.data(), src.size());
  300|  2.04k|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|  2.04k|        ret = impl->count_utf8(src.data(), src.size());
  302|  2.04k|      }
  303|  2.04k|      results.push_back(ret);
  304|  2.04k|    }
  305|    681|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    681|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 681]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    681|    return true;
  321|    681|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|  1.36k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    681|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    681|    length_result ret{};
  344|       |
  345|    681|    const auto implementations = get_supported_implementations();
  346|    681|    std::vector<std::size_t> results;
  347|    681|    results.reserve(implementations.size());
  348|       |
  349|  2.04k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 2.04k, False: 681]
  ------------------
  350|  2.04k|      const auto len = invoke_lengthcalc(impl, src);
  351|  2.04k|      results.push_back(len);
  352|  2.04k|      ret.length.push_back(len);
  353|  2.04k|    }
  354|       |
  355|    681|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    681|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 681]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    681|    } else {
  375|    681|      ret.implementations_agree = true;
  376|    681|    }
  377|    681|    return ret;
  378|    681|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  2.04k|                                FromSpan src) const {
  331|  2.04k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  2.04k|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|  1.36k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    665|                                  const bool inputisvalid) const {
  383|    665|    conversion_result ret{};
  384|       |
  385|    665|    const auto implementations = get_supported_implementations();
  386|       |
  387|    665|    std::vector<result<ConversionResult>> results;
  388|    665|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    665|    std::vector<std::vector<ToType>> outputbuffers;
  393|    665|    outputbuffers.reserve(implementations.size());
  394|  2.66k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.99k, False: 665]
  ------------------
  395|  1.99k|      auto impl = implementations[i];
  396|  1.99k|      const ToType canary1{42};
  397|  1.99k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.99k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.99k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.99k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.99k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.99k|          return r != 0;
  404|  1.99k|        } else {
  405|  1.99k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.99k|        }
  407|  1.99k|      }(implret1);
  408|  1.99k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.99k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  1.99k|        const ToType canary2{25};
  414|  1.99k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.99k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.99k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.99k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.99k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.99k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.99k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 948, False: 1.04k]
  |  Branch (423:29): [True: 939, False: 9]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    939|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    939|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 939]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    939|        }
  440|  1.99k|      }
  441|  1.99k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 939, False: 1.05k]
  ------------------
  442|  1.99k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    665|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 349, False: 316]
  ------------------
  447|  1.04k|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 1.04k, False: 349]
  ------------------
  448|  1.04k|        e.outputhash.clear();
  449|  1.04k|      }
  450|    349|    }
  451|       |
  452|    665|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    665|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 665]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    665|    } else {
  474|    665|      ret.implementations_agree = true;
  475|    665|    }
  476|    665|    return ret;
  477|    665|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|  1.99k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.99k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.99k|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  1.99k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|  1.33k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_9clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    259|    +[](std::span<const char> chardata) {                                      \
  555|    259|      const auto c =                                                           \
  556|    259|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    259|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    259|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    259|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    259|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    259|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    259|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    259|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    259|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    259|  constexpr auto _size = _name.size();                                \
  |  | 1183|    259|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    259|  return _nameof; }()
  ------------------
  561|    259|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    259|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    259|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    259|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    259|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    259|  constexpr auto _size = _name.size();                                \
  |  | 1183|    259|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    259|  return _nameof; }()
  ------------------
  562|    259|      c.fuzz(chardata);                                                        \
  563|    259|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    737|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    737|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    737|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    737|    static const bool do_print_testcase =
  181|    737|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    737|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 737]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    737|    do {
  189|       |      // step 0 - is the input valid?
  190|    737|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    737|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 737]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    737|                    From == UtfEncodings::UTF8) {
  198|    737|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 737]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    737|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    737|      const auto [output_length, length_agree] =
  204|    737|          calculate_length(from, inputisvalid);
  205|    737|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 737]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    737|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 349, False: 388]
  |  Branch (208:28): [True: 6, False: 343]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      6|        return;
  211|      6|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    731|      const auto [written, outputs_agree] =
  215|    731|          do_conversion(from, output_length, inputisvalid);
  216|    731|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 731]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    731|      return;
  221|    731|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    737|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    737|  validation_result verify_valid_input(FromSpan src) const {
  231|    737|    validation_result ret{};
  232|       |
  233|    737|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    737|    const auto implementations = get_supported_implementations();
  235|    737|    std::vector<simdutf::result> results;
  236|    737|    results.reserve(implementations.size());
  237|       |
  238|  2.21k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 2.21k, False: 737]
  ------------------
  239|  2.21k|      results.push_back(
  240|  2.21k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  2.21k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  2.21k|      const bool validation2 =
  245|  2.21k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  2.21k|                      src.data(), src.size());
  247|  2.21k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 2.21k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  2.21k|    }
  258|       |
  259|    737|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    737|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 737]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    737|    } else {
  273|    737|      ret.implementations_agree = true;
  274|    737|    }
  275|    737|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    737|      return r.error == simdutf::SUCCESS;
  277|    737|    });
  278|    737|    return ret;
  279|    737|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|  1.47k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKNS1_6resultEE_clESI_:
  275|  1.51k|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|  1.51k|      return r.error == simdutf::SUCCESS;
  277|  1.51k|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    737|  bool count_the_input(FromSpan src) const {
  290|    737|    const auto implementations = get_supported_implementations();
  291|    737|    std::vector<std::size_t> results;
  292|    737|    results.reserve(implementations.size());
  293|       |
  294|  2.21k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 2.21k, False: 737]
  ------------------
  295|  2.21k|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|       |      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|       |        ret = impl->count_utf16le(src.data(), src.size());
  300|  2.21k|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|  2.21k|        ret = impl->count_utf8(src.data(), src.size());
  302|  2.21k|      }
  303|  2.21k|      results.push_back(ret);
  304|  2.21k|    }
  305|    737|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    737|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 737]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    737|    return true;
  321|    737|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|  1.47k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    737|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    737|    length_result ret{};
  344|       |
  345|    737|    const auto implementations = get_supported_implementations();
  346|    737|    std::vector<std::size_t> results;
  347|    737|    results.reserve(implementations.size());
  348|       |
  349|  2.21k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 2.21k, False: 737]
  ------------------
  350|  2.21k|      const auto len = invoke_lengthcalc(impl, src);
  351|  2.21k|      results.push_back(len);
  352|  2.21k|      ret.length.push_back(len);
  353|  2.21k|    }
  354|       |
  355|    737|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    737|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 737]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    737|    } else {
  375|    737|      ret.implementations_agree = true;
  376|    737|    }
  377|    737|    return ret;
  378|    737|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  2.21k|                                FromSpan src) const {
  331|  2.21k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  2.21k|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|  1.47k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    731|                                  const bool inputisvalid) const {
  383|    731|    conversion_result ret{};
  384|       |
  385|    731|    const auto implementations = get_supported_implementations();
  386|       |
  387|    731|    std::vector<result<ConversionResult>> results;
  388|    731|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    731|    std::vector<std::vector<ToType>> outputbuffers;
  393|    731|    outputbuffers.reserve(implementations.size());
  394|  2.92k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 2.19k, False: 731]
  ------------------
  395|  2.19k|      auto impl = implementations[i];
  396|  2.19k|      const ToType canary1{42};
  397|  2.19k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  2.19k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  2.19k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  2.19k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  2.19k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  2.19k|          return r != 0;
  404|  2.19k|        } else {
  405|  2.19k|          return r.error == simdutf::error_code::SUCCESS;
  406|  2.19k|        }
  407|  2.19k|      }(implret1);
  408|  2.19k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  2.19k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  2.19k|        const ToType canary2{25};
  414|  2.19k|        const auto outputbuffer_first_run = outputbuffer;
  415|  2.19k|        std::ranges::fill(outputbuffer, canary2);
  416|  2.19k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  2.19k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  2.19k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 2.19k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  2.19k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 1.16k, False: 1.02k]
  |  Branch (423:29): [True: 1.15k, False: 9]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|  1.15k|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|  1.15k|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 1.15k]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|  1.15k|        }
  440|  2.19k|      }
  441|  2.19k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 1.15k, False: 1.03k]
  ------------------
  442|  2.19k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    731|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 343, False: 388]
  ------------------
  447|  1.02k|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 1.02k, False: 343]
  ------------------
  448|  1.02k|        e.outputhash.clear();
  449|  1.02k|      }
  450|    343|    }
  451|       |
  452|    731|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    731|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 731]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    731|    } else {
  474|    731|      ret.implementations_agree = true;
  475|    731|    }
  476|    731|    return ret;
  477|    731|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|  2.19k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  2.19k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  2.19k|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  2.19k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|  1.46k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_10clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     49|    +[](std::span<const char> chardata) {                                      \
  555|     49|      const auto c =                                                           \
  556|     49|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     49|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     49|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     49|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     49|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     49|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     49|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     49|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     49|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     49|  constexpr auto _size = _name.size();                                \
  |  | 1183|     49|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     49|  return _nameof; }()
  ------------------
  561|     49|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     49|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     49|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     49|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     49|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     49|  constexpr auto _size = _name.size();                                \
  |  | 1183|     49|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     49|  return _nameof; }()
  ------------------
  562|     49|      c.fuzz(chardata);                                                        \
  563|     49|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|     49|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|     49|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|     49|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|     49|    static const bool do_print_testcase =
  181|     49|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|     49|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 49]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|     49|    do {
  189|       |      // step 0 - is the input valid?
  190|     49|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|     49|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 49]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|     49|                    From == UtfEncodings::UTF8) {
  198|     49|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 49]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|     49|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|     49|      const auto [output_length, length_agree] =
  204|     49|          calculate_length(from, inputisvalid);
  205|     49|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 49]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|     49|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 6, False: 43]
  |  Branch (208:28): [True: 0, False: 6]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|     49|      const auto [written, outputs_agree] =
  215|     49|          do_conversion(from, output_length, inputisvalid);
  216|     49|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 49]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|     49|      return;
  221|     49|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|     49|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEE:
  230|     49|  validation_result verify_valid_input(FromSpan src) const {
  231|     49|    validation_result ret{};
  232|       |
  233|     49|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|     49|    const auto implementations = get_supported_implementations();
  235|     49|    std::vector<simdutf::result> results;
  236|     49|    results.reserve(implementations.size());
  237|       |
  238|    147|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 147, False: 49]
  ------------------
  239|    147|      results.push_back(
  240|    147|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    147|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    147|      const bool validation2 =
  245|    147|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    147|                      src.data(), src.size());
  247|    147|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 147]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|    147|    }
  258|       |
  259|     49|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|     49|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 49]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|     49|    } else {
  273|     49|      ret.implementations_agree = true;
  274|     49|    }
  275|     49|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|     49|      return r.error == simdutf::SUCCESS;
  277|     49|    });
  278|     49|    return ret;
  279|     49|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|     98|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEEENKUlRKNS1_6resultEE_clESI_:
  275|    135|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    135|      return r.error == simdutf::SUCCESS;
  277|    135|    });
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE15count_the_inputENSt3__14spanIS5_Lm18446744073709551615EEE:
  289|     49|  bool count_the_input(FromSpan src) const {
  290|     49|    const auto implementations = get_supported_implementations();
  291|     49|    std::vector<std::size_t> results;
  292|     49|    results.reserve(implementations.size());
  293|       |
  294|    147|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 147, False: 49]
  ------------------
  295|    147|      std::size_t ret;
  296|    147|      if constexpr (From == UtfEncodings::UTF16BE) {
  297|    147|        ret = impl->count_utf16be(src.data(), src.size());
  298|       |      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|       |        ret = impl->count_utf16le(src.data(), src.size());
  300|       |      } else if constexpr (From == UtfEncodings::UTF8) {
  301|       |        ret = impl->count_utf8(src.data(), src.size());
  302|       |      }
  303|    147|      results.push_back(ret);
  304|    147|    }
  305|     49|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|     49|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 49]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|     49|    return true;
  321|     49|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE15count_the_inputENSt3__14spanIS5_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|     98|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEb:
  342|     49|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|     49|    length_result ret{};
  344|       |
  345|     49|    const auto implementations = get_supported_implementations();
  346|     49|    std::vector<std::size_t> results;
  347|     49|    results.reserve(implementations.size());
  348|       |
  349|    147|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 147, False: 49]
  ------------------
  350|    147|      const auto len = invoke_lengthcalc(impl, src);
  351|    147|      results.push_back(len);
  352|    147|      ret.length.push_back(len);
  353|    147|    }
  354|       |
  355|     49|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|     49|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 49]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|     49|    } else {
  375|     49|      ret.implementations_agree = true;
  376|     49|    }
  377|     49|    return ret;
  378|     49|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSE_NSt3__14spanIS5_Lm18446744073709551615EEE:
  338|    147|                                FromSpan src) const {
  339|    147|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    147|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|     98|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|     49|                                  const bool inputisvalid) const {
  383|     49|    conversion_result ret{};
  384|       |
  385|     49|    const auto implementations = get_supported_implementations();
  386|       |
  387|     49|    std::vector<result<ConversionResult>> results;
  388|     49|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|     49|    std::vector<std::vector<ToType>> outputbuffers;
  393|     49|    outputbuffers.reserve(implementations.size());
  394|    196|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 147, False: 49]
  ------------------
  395|    147|      auto impl = implementations[i];
  396|    147|      const ToType canary1{42};
  397|    147|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    147|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    147|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    147|      const auto success = [](const ConversionResult& r) -> bool {
  402|    147|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    147|          return r != 0;
  404|    147|        } else {
  405|    147|          return r.error == simdutf::error_code::SUCCESS;
  406|    147|        }
  407|    147|      }(implret1);
  408|    147|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    147|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    147|        const ToType canary2{25};
  414|    147|        const auto outputbuffer_first_run = outputbuffer;
  415|    147|        std::ranges::fill(outputbuffer, canary2);
  416|    147|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    147|                                          src.size(), outputbuffer.data());
  418|       |
  419|    147|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 147]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    147|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 129, False: 18]
  |  Branch (423:29): [True: 69, False: 60]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|     69|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|     69|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 69]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|     69|        }
  440|    147|      }
  441|    147|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 69, False: 78]
  ------------------
  442|    147|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|     49|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 6, False: 43]
  ------------------
  447|     18|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 18, False: 6]
  ------------------
  448|     18|        e.outputhash.clear();
  449|     18|      }
  450|      6|    }
  451|       |
  452|     49|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|     49|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 49]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|     49|    } else {
  474|     49|      ret.implementations_agree = true;
  475|     49|    }
  476|     49|    return ret;
  477|     49|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    147|      const auto success = [](const ConversionResult& r) -> bool {
  402|    147|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    147|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    147|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|     98|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_11clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    139|    +[](std::span<const char> chardata) {                                      \
  555|    139|      const auto c =                                                           \
  556|    139|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    139|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    139|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    139|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    139|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    139|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    139|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    139|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    139|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    139|  constexpr auto _size = _name.size();                                \
  |  | 1183|    139|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    139|  return _nameof; }()
  ------------------
  561|    139|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    139|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    139|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    139|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    139|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    139|  constexpr auto _size = _name.size();                                \
  |  | 1183|    139|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    139|  return _nameof; }()
  ------------------
  562|    139|      c.fuzz(chardata);                                                        \
  563|    139|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_12clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    198|    +[](std::span<const char> chardata) {                                      \
  555|    198|      const auto c =                                                           \
  556|    198|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    198|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    198|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    198|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    198|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    198|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    198|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    198|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    198|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    198|  constexpr auto _size = _name.size();                                \
  |  | 1183|    198|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    198|  return _nameof; }()
  ------------------
  561|    198|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    198|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    198|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    198|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    198|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    198|  constexpr auto _size = _name.size();                                \
  |  | 1183|    198|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    198|  return _nameof; }()
  ------------------
  562|    198|      c.fuzz(chardata);                                                        \
  563|    198|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_13clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     60|    +[](std::span<const char> chardata) {                                      \
  555|     60|      const auto c =                                                           \
  556|     60|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     60|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     60|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     60|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     60|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     60|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     60|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     60|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     60|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     60|  constexpr auto _size = _name.size();                                \
  |  | 1183|     60|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     60|  return _nameof; }()
  ------------------
  561|     60|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     60|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     60|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     60|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     60|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     60|  constexpr auto _size = _name.size();                                \
  |  | 1183|     60|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     60|  return _nameof; }()
  ------------------
  562|     60|      c.fuzz(chardata);                                                        \
  563|     60|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|     60|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|     60|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|     60|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|     60|    static const bool do_print_testcase =
  181|     60|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|     60|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 60]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|     60|    do {
  189|       |      // step 0 - is the input valid?
  190|     60|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|     60|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 60]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|     60|                    From == UtfEncodings::UTF8) {
  198|     60|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 60]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|     60|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|     60|      const auto [output_length, length_agree] =
  204|     60|          calculate_length(from, inputisvalid);
  205|     60|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 60]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|     60|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 14, False: 46]
  |  Branch (208:28): [True: 0, False: 14]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|     60|      const auto [written, outputs_agree] =
  215|     60|          do_conversion(from, output_length, inputisvalid);
  216|     60|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 60]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|     60|      return;
  221|     60|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|     60|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEE:
  230|     60|  validation_result verify_valid_input(FromSpan src) const {
  231|     60|    validation_result ret{};
  232|       |
  233|     60|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|     60|    const auto implementations = get_supported_implementations();
  235|     60|    std::vector<simdutf::result> results;
  236|     60|    results.reserve(implementations.size());
  237|       |
  238|    180|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 180, False: 60]
  ------------------
  239|    180|      results.push_back(
  240|    180|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    180|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    180|      const bool validation2 =
  245|    180|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    180|                      src.data(), src.size());
  247|    180|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 180]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|    180|    }
  258|       |
  259|     60|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|     60|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 60]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|     60|    } else {
  273|     60|      ret.implementations_agree = true;
  274|     60|    }
  275|     60|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|     60|      return r.error == simdutf::SUCCESS;
  277|     60|    });
  278|     60|    return ret;
  279|     60|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    120|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEEENKUlRKNS1_6resultEE_clESI_:
  275|    152|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    152|      return r.error == simdutf::SUCCESS;
  277|    152|    });
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE15count_the_inputENSt3__14spanIS5_Lm18446744073709551615EEE:
  289|     60|  bool count_the_input(FromSpan src) const {
  290|     60|    const auto implementations = get_supported_implementations();
  291|     60|    std::vector<std::size_t> results;
  292|     60|    results.reserve(implementations.size());
  293|       |
  294|    180|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 180, False: 60]
  ------------------
  295|    180|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|    180|      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|    180|        ret = impl->count_utf16le(src.data(), src.size());
  300|       |      } else if constexpr (From == UtfEncodings::UTF8) {
  301|       |        ret = impl->count_utf8(src.data(), src.size());
  302|       |      }
  303|    180|      results.push_back(ret);
  304|    180|    }
  305|     60|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|     60|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 60]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|     60|    return true;
  321|     60|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE15count_the_inputENSt3__14spanIS5_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|    120|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEb:
  342|     60|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|     60|    length_result ret{};
  344|       |
  345|     60|    const auto implementations = get_supported_implementations();
  346|     60|    std::vector<std::size_t> results;
  347|     60|    results.reserve(implementations.size());
  348|       |
  349|    180|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 180, False: 60]
  ------------------
  350|    180|      const auto len = invoke_lengthcalc(impl, src);
  351|    180|      results.push_back(len);
  352|    180|      ret.length.push_back(len);
  353|    180|    }
  354|       |
  355|     60|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|     60|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 60]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|     60|    } else {
  375|     60|      ret.implementations_agree = true;
  376|     60|    }
  377|     60|    return ret;
  378|     60|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSE_NSt3__14spanIS5_Lm18446744073709551615EEE:
  338|    180|                                FromSpan src) const {
  339|    180|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    180|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    120|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|     60|                                  const bool inputisvalid) const {
  383|     60|    conversion_result ret{};
  384|       |
  385|     60|    const auto implementations = get_supported_implementations();
  386|       |
  387|     60|    std::vector<result<ConversionResult>> results;
  388|     60|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|     60|    std::vector<std::vector<ToType>> outputbuffers;
  393|     60|    outputbuffers.reserve(implementations.size());
  394|    240|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 180, False: 60]
  ------------------
  395|    180|      auto impl = implementations[i];
  396|    180|      const ToType canary1{42};
  397|    180|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    180|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    180|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    180|      const auto success = [](const ConversionResult& r) -> bool {
  402|    180|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    180|          return r != 0;
  404|    180|        } else {
  405|    180|          return r.error == simdutf::error_code::SUCCESS;
  406|    180|        }
  407|    180|      }(implret1);
  408|    180|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    180|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    180|        const ToType canary2{25};
  414|    180|        const auto outputbuffer_first_run = outputbuffer;
  415|    180|        std::ranges::fill(outputbuffer, canary2);
  416|    180|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    180|                                          src.size(), outputbuffer.data());
  418|       |
  419|    180|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 180]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    180|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 138, False: 42]
  |  Branch (423:29): [True: 63, False: 75]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|     63|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|     63|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 63]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|     63|        }
  440|    180|      }
  441|    180|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 63, False: 117]
  ------------------
  442|    180|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|     60|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 14, False: 46]
  ------------------
  447|     42|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 42, False: 14]
  ------------------
  448|     42|        e.outputhash.clear();
  449|     42|      }
  450|     14|    }
  451|       |
  452|     60|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|     60|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 60]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|     60|    } else {
  474|     60|      ret.implementations_agree = true;
  475|     60|    }
  476|     60|    return ret;
  477|     60|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    180|      const auto success = [](const ConversionResult& r) -> bool {
  402|    180|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    180|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    180|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    120|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_14clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    151|    +[](std::span<const char> chardata) {                                      \
  555|    151|      const auto c =                                                           \
  556|    151|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    151|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    151|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    151|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    151|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    151|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    151|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    151|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    151|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    151|  constexpr auto _size = _name.size();                                \
  |  | 1183|    151|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    151|  return _nameof; }()
  ------------------
  561|    151|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    151|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    151|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    151|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    151|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    151|  constexpr auto _size = _name.size();                                \
  |  | 1183|    151|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    151|  return _nameof; }()
  ------------------
  562|    151|      c.fuzz(chardata);                                                        \
  563|    151|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_15clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    236|    +[](std::span<const char> chardata) {                                      \
  555|    236|      const auto c =                                                           \
  556|    236|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    236|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    236|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    236|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    236|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    236|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    236|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    236|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    236|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    236|  constexpr auto _size = _name.size();                                \
  |  | 1183|    236|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    236|  return _nameof; }()
  ------------------
  561|    236|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    236|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    236|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    236|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    236|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    236|  constexpr auto _size = _name.size();                                \
  |  | 1183|    236|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    236|  return _nameof; }()
  ------------------
  562|    236|      c.fuzz(chardata);                                                        \
  563|    236|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_16clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     93|    +[](std::span<const char> chardata) {                                      \
  555|     93|      const auto c =                                                           \
  556|     93|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     93|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     93|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     93|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     93|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     93|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     93|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     93|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     93|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     93|  constexpr auto _size = _name.size();                                \
  |  | 1183|     93|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     93|  return _nameof; }()
  ------------------
  561|     93|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     93|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     93|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     93|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     93|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     93|  constexpr auto _size = _name.size();                                \
  |  | 1183|     93|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     93|  return _nameof; }()
  ------------------
  562|     93|      c.fuzz(chardata);                                                        \
  563|     93|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|     93|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|     93|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|     93|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|     93|    static const bool do_print_testcase =
  181|     93|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|     93|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 93]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|     93|    do {
  189|       |      // step 0 - is the input valid?
  190|     93|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|     93|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 93]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|       |                    From == UtfEncodings::UTF8) {
  198|       |        if (!count_the_input(from) && !allow_implementations_to_differ)
  199|       |          break;
  200|       |      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|     93|      const auto [output_length, length_agree] =
  204|     93|          calculate_length(from, inputisvalid);
  205|     93|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 93]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|     93|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 68, False: 25]
  |  Branch (208:28): [True: 0, False: 68]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|     93|      const auto [written, outputs_agree] =
  215|     93|          do_conversion(from, output_length, inputisvalid);
  216|     93|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 93]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|     93|      return;
  221|     93|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|     93|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEE:
  230|     93|  validation_result verify_valid_input(FromSpan src) const {
  231|     93|    validation_result ret{};
  232|       |
  233|     93|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|     93|    const auto implementations = get_supported_implementations();
  235|     93|    std::vector<simdutf::result> results;
  236|     93|    results.reserve(implementations.size());
  237|       |
  238|    279|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 279, False: 93]
  ------------------
  239|    279|      results.push_back(
  240|    279|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    279|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    279|      const bool validation2 =
  245|    279|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    279|                      src.data(), src.size());
  247|    279|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 279]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|    279|    }
  258|       |
  259|     93|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|     93|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 93]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|     93|    } else {
  273|     93|      ret.implementations_agree = true;
  274|     93|    }
  275|     93|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|     93|      return r.error == simdutf::SUCCESS;
  277|     93|    });
  278|     93|    return ret;
  279|     93|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    186|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEEENKUlRKNS1_6resultEE_clESI_:
  275|    143|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    143|      return r.error == simdutf::SUCCESS;
  277|    143|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEb:
  342|     93|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|     93|    length_result ret{};
  344|       |
  345|     93|    const auto implementations = get_supported_implementations();
  346|     93|    std::vector<std::size_t> results;
  347|     93|    results.reserve(implementations.size());
  348|       |
  349|    279|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 279, False: 93]
  ------------------
  350|    279|      const auto len = invoke_lengthcalc(impl, src);
  351|    279|      results.push_back(len);
  352|    279|      ret.length.push_back(len);
  353|    279|    }
  354|       |
  355|     93|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|     93|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 93]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|     93|    } else {
  375|     93|      ret.implementations_agree = true;
  376|     93|    }
  377|     93|    return ret;
  378|     93|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSE_NSt3__14spanIS5_Lm18446744073709551615EEE:
  338|    279|                                FromSpan src) const {
  339|    279|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    279|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    186|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|     93|                                  const bool inputisvalid) const {
  383|     93|    conversion_result ret{};
  384|       |
  385|     93|    const auto implementations = get_supported_implementations();
  386|       |
  387|     93|    std::vector<result<ConversionResult>> results;
  388|     93|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|     93|    std::vector<std::vector<ToType>> outputbuffers;
  393|     93|    outputbuffers.reserve(implementations.size());
  394|    372|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 279, False: 93]
  ------------------
  395|    279|      auto impl = implementations[i];
  396|    279|      const ToType canary1{42};
  397|    279|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    279|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    279|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    279|      const auto success = [](const ConversionResult& r) -> bool {
  402|    279|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    279|          return r != 0;
  404|    279|        } else {
  405|    279|          return r.error == simdutf::error_code::SUCCESS;
  406|    279|        }
  407|    279|      }(implret1);
  408|    279|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    279|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    279|        const ToType canary2{25};
  414|    279|        const auto outputbuffer_first_run = outputbuffer;
  415|    279|        std::ranges::fill(outputbuffer, canary2);
  416|    279|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    279|                                          src.size(), outputbuffer.data());
  418|       |
  419|    279|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 279]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    279|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 75, False: 204]
  |  Branch (423:29): [True: 57, False: 18]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|     57|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|     57|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 57]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|     57|        }
  440|    279|      }
  441|    279|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 57, False: 222]
  ------------------
  442|    279|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|     93|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 68, False: 25]
  ------------------
  447|    204|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 204, False: 68]
  ------------------
  448|    204|        e.outputhash.clear();
  449|    204|      }
  450|     68|    }
  451|       |
  452|     93|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|     93|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 93]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|     93|    } else {
  474|     93|      ret.implementations_agree = true;
  475|     93|    }
  476|     93|    return ret;
  477|     93|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    279|      const auto success = [](const ConversionResult& r) -> bool {
  402|    279|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    279|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    279|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    186|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_17clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    244|    +[](std::span<const char> chardata) {                                      \
  555|    244|      const auto c =                                                           \
  556|    244|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    244|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    244|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    244|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    244|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    244|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    244|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    244|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    244|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    244|  constexpr auto _size = _name.size();                                \
  |  | 1183|    244|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    244|  return _nameof; }()
  ------------------
  561|    244|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    244|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    244|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    244|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    244|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    244|  constexpr auto _size = _name.size();                                \
  |  | 1183|    244|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    244|  return _nameof; }()
  ------------------
  562|    244|      c.fuzz(chardata);                                                        \
  563|    244|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_18clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    243|    +[](std::span<const char> chardata) {                                      \
  555|    243|      const auto c =                                                           \
  556|    243|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    243|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    243|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    243|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    243|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    243|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    243|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    243|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    243|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    243|  constexpr auto _size = _name.size();                                \
  |  | 1183|    243|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    243|  return _nameof; }()
  ------------------
  561|    243|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    243|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    243|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    243|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    243|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    243|  constexpr auto _size = _name.size();                                \
  |  | 1183|    243|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    243|  return _nameof; }()
  ------------------
  562|    243|      c.fuzz(chardata);                                                        \
  563|    243|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_19clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    363|    +[](std::span<const char> chardata) {                                      \
  555|    363|      const auto c =                                                           \
  556|    363|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    363|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    363|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    363|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    363|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    363|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    363|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    363|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    363|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    363|  constexpr auto _size = _name.size();                                \
  |  | 1183|    363|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    363|  return _nameof; }()
  ------------------
  561|    363|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    363|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    363|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    363|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    363|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    363|  constexpr auto _size = _name.size();                                \
  |  | 1183|    363|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    363|  return _nameof; }()
  ------------------
  562|    363|      c.fuzz(chardata);                                                        \
  563|    363|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_20clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    355|    +[](std::span<const char> chardata) {                                      \
  555|    355|      const auto c =                                                           \
  556|    355|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    355|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    355|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    355|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    355|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    355|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    355|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    355|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    355|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    355|  constexpr auto _size = _name.size();                                \
  |  | 1183|    355|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    355|  return _nameof; }()
  ------------------
  561|    355|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    355|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    355|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    355|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    355|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    355|  constexpr auto _size = _name.size();                                \
  |  | 1183|    355|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    355|  return _nameof; }()
  ------------------
  562|    355|      c.fuzz(chardata);                                                        \
  563|    355|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    355|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    355|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    355|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    355|    static const bool do_print_testcase =
  181|    355|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    355|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 355]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    355|    do {
  189|       |      // step 0 - is the input valid?
  190|    355|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    355|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 355]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    355|                    From == UtfEncodings::UTF8) {
  198|    355|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 355]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    355|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    355|      const auto [output_length, length_agree] =
  204|    355|          calculate_length(from, inputisvalid);
  205|    355|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 355]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    355|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 246, False: 109]
  |  Branch (208:28): [True: 0, False: 246]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    355|      const auto [written, outputs_agree] =
  215|    355|          do_conversion(from, output_length, inputisvalid);
  216|    355|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 355]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    355|      return;
  221|    355|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    355|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    355|  validation_result verify_valid_input(FromSpan src) const {
  231|    355|    validation_result ret{};
  232|       |
  233|    355|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    355|    const auto implementations = get_supported_implementations();
  235|    355|    std::vector<simdutf::result> results;
  236|    355|    results.reserve(implementations.size());
  237|       |
  238|  1.06k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.06k, False: 355]
  ------------------
  239|  1.06k|      results.push_back(
  240|  1.06k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.06k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.06k|      const bool validation2 =
  245|  1.06k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.06k|                      src.data(), src.size());
  247|  1.06k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.06k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  1.06k|    }
  258|       |
  259|    355|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    355|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 355]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    355|    } else {
  273|    355|      ret.implementations_agree = true;
  274|    355|    }
  275|    355|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    355|      return r.error == simdutf::SUCCESS;
  277|    355|    });
  278|    355|    return ret;
  279|    355|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    710|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKNS1_6resultEE_clESI_:
  275|    573|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    573|      return r.error == simdutf::SUCCESS;
  277|    573|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    355|  bool count_the_input(FromSpan src) const {
  290|    355|    const auto implementations = get_supported_implementations();
  291|    355|    std::vector<std::size_t> results;
  292|    355|    results.reserve(implementations.size());
  293|       |
  294|  1.06k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.06k, False: 355]
  ------------------
  295|  1.06k|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|       |      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|       |        ret = impl->count_utf16le(src.data(), src.size());
  300|  1.06k|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|  1.06k|        ret = impl->count_utf8(src.data(), src.size());
  302|  1.06k|      }
  303|  1.06k|      results.push_back(ret);
  304|  1.06k|    }
  305|    355|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    355|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 355]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    355|    return true;
  321|    355|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|    710|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    355|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    355|    length_result ret{};
  344|       |
  345|    355|    const auto implementations = get_supported_implementations();
  346|    355|    std::vector<std::size_t> results;
  347|    355|    results.reserve(implementations.size());
  348|       |
  349|  1.06k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.06k, False: 355]
  ------------------
  350|  1.06k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.06k|      results.push_back(len);
  352|  1.06k|      ret.length.push_back(len);
  353|  1.06k|    }
  354|       |
  355|    355|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    355|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 355]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    355|    } else {
  375|    355|      ret.implementations_agree = true;
  376|    355|    }
  377|    355|    return ret;
  378|    355|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.06k|                                FromSpan src) const {
  331|  1.06k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.06k|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    710|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    355|                                  const bool inputisvalid) const {
  383|    355|    conversion_result ret{};
  384|       |
  385|    355|    const auto implementations = get_supported_implementations();
  386|       |
  387|    355|    std::vector<result<ConversionResult>> results;
  388|    355|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    355|    std::vector<std::vector<ToType>> outputbuffers;
  393|    355|    outputbuffers.reserve(implementations.size());
  394|  1.42k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.06k, False: 355]
  ------------------
  395|  1.06k|      auto impl = implementations[i];
  396|  1.06k|      const ToType canary1{42};
  397|  1.06k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.06k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.06k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.06k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.06k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.06k|          return r != 0;
  404|  1.06k|        } else {
  405|  1.06k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.06k|        }
  407|  1.06k|      }(implret1);
  408|  1.06k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.06k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  1.06k|        const ToType canary2{25};
  414|  1.06k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.06k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.06k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.06k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.06k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.06k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.06k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 327, False: 738]
  |  Branch (423:29): [True: 300, False: 27]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    300|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    300|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 300]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    300|        }
  440|  1.06k|      }
  441|  1.06k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 300, False: 765]
  ------------------
  442|  1.06k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    355|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 246, False: 109]
  ------------------
  447|    738|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 738, False: 246]
  ------------------
  448|    738|        e.outputhash.clear();
  449|    738|      }
  450|    246|    }
  451|       |
  452|    355|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    355|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 355]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    355|    } else {
  474|    355|      ret.implementations_agree = true;
  475|    355|    }
  476|    355|    return ret;
  477|    355|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|  1.06k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.06k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.06k|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  1.06k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    710|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_21clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    504|    +[](std::span<const char> chardata) {                                      \
  555|    504|      const auto c =                                                           \
  556|    504|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    504|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    504|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    504|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    504|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    504|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    504|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    504|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    504|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    504|  constexpr auto _size = _name.size();                                \
  |  | 1183|    504|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    504|  return _nameof; }()
  ------------------
  561|    504|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    504|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    504|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    504|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    504|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    504|  constexpr auto _size = _name.size();                                \
  |  | 1183|    504|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    504|  return _nameof; }()
  ------------------
  562|    504|      c.fuzz(chardata);                                                        \
  563|    504|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_22clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    475|    +[](std::span<const char> chardata) {                                      \
  555|    475|      const auto c =                                                           \
  556|    475|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    475|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    475|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    475|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    475|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    475|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    475|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    475|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    475|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    475|  constexpr auto _size = _name.size();                                \
  |  | 1183|    475|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    475|  return _nameof; }()
  ------------------
  561|    475|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    475|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    475|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    475|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    475|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    475|  constexpr auto _size = _name.size();                                \
  |  | 1183|    475|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    475|  return _nameof; }()
  ------------------
  562|    475|      c.fuzz(chardata);                                                        \
  563|    475|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_23clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    478|    +[](std::span<const char> chardata) {                                      \
  555|    478|      const auto c =                                                           \
  556|    478|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    478|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    478|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    478|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    478|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    478|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    478|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    478|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    478|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    478|  constexpr auto _size = _name.size();                                \
  |  | 1183|    478|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    478|  return _nameof; }()
  ------------------
  561|    478|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    478|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    478|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    478|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    478|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    478|  constexpr auto _size = _name.size();                                \
  |  | 1183|    478|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    478|  return _nameof; }()
  ------------------
  562|    478|      c.fuzz(chardata);                                                        \
  563|    478|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_24clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    124|    +[](std::span<const char> chardata) {                                      \
  555|    124|      const auto c =                                                           \
  556|    124|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    124|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    124|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    124|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    124|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    124|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    124|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    124|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    124|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    124|  constexpr auto _size = _name.size();                                \
  |  | 1183|    124|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    124|  return _nameof; }()
  ------------------
  561|    124|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    124|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    124|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    124|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    124|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    124|  constexpr auto _size = _name.size();                                \
  |  | 1183|    124|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    124|  return _nameof; }()
  ------------------
  562|    124|      c.fuzz(chardata);                                                        \
  563|    124|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    124|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    124|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    124|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    124|    static const bool do_print_testcase =
  181|    124|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    124|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 124]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    124|    do {
  189|       |      // step 0 - is the input valid?
  190|    124|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    124|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 124]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    124|                    From == UtfEncodings::UTF8) {
  198|    124|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 124]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    124|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    124|      const auto [output_length, length_agree] =
  204|    124|          calculate_length(from, inputisvalid);
  205|    124|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 124]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    124|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 22, False: 102]
  |  Branch (208:28): [True: 0, False: 22]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    124|      const auto [written, outputs_agree] =
  215|    124|          do_conversion(from, output_length, inputisvalid);
  216|    124|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 124]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    124|      return;
  221|    124|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    124|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS6_Lm18446744073709551615EEE:
  230|    124|  validation_result verify_valid_input(FromSpan src) const {
  231|    124|    validation_result ret{};
  232|       |
  233|    124|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    124|    const auto implementations = get_supported_implementations();
  235|    124|    std::vector<simdutf::result> results;
  236|    124|    results.reserve(implementations.size());
  237|       |
  238|    372|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 372, False: 124]
  ------------------
  239|    372|      results.push_back(
  240|    372|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    372|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    372|      const bool validation2 =
  245|    372|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    372|                      src.data(), src.size());
  247|    372|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 372]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|    372|    }
  258|       |
  259|    124|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    124|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 124]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    124|    } else {
  273|    124|      ret.implementations_agree = true;
  274|    124|    }
  275|    124|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    124|      return r.error == simdutf::SUCCESS;
  277|    124|    });
  278|    124|    return ret;
  279|    124|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS6_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS5_S5_EEDaSJ_SM_:
  259|    248|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS6_Lm18446744073709551615EEEENKUlRKS5_E_clESI_:
  275|    328|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    328|      return r.error == simdutf::SUCCESS;
  277|    328|    });
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE15count_the_inputENSt3__14spanIS6_Lm18446744073709551615EEE:
  289|    124|  bool count_the_input(FromSpan src) const {
  290|    124|    const auto implementations = get_supported_implementations();
  291|    124|    std::vector<std::size_t> results;
  292|    124|    results.reserve(implementations.size());
  293|       |
  294|    372|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 372, False: 124]
  ------------------
  295|    372|      std::size_t ret;
  296|    372|      if constexpr (From == UtfEncodings::UTF16BE) {
  297|    372|        ret = impl->count_utf16be(src.data(), src.size());
  298|       |      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|       |        ret = impl->count_utf16le(src.data(), src.size());
  300|       |      } else if constexpr (From == UtfEncodings::UTF8) {
  301|       |        ret = impl->count_utf8(src.data(), src.size());
  302|       |      }
  303|    372|      results.push_back(ret);
  304|    372|    }
  305|    124|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    124|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 124]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    124|    return true;
  321|    124|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE15count_the_inputENSt3__14spanIS6_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    248|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE16calculate_lengthENSt3__14spanIS6_Lm18446744073709551615EEEb:
  342|    124|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    124|    length_result ret{};
  344|       |
  345|    124|    const auto implementations = get_supported_implementations();
  346|    124|    std::vector<std::size_t> results;
  347|    124|    results.reserve(implementations.size());
  348|       |
  349|    372|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 372, False: 124]
  ------------------
  350|    372|      const auto len = invoke_lengthcalc(impl, src);
  351|    372|      results.push_back(len);
  352|    372|      ret.length.push_back(len);
  353|    372|    }
  354|       |
  355|    124|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    124|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 124]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    124|    } else {
  375|    124|      ret.implementations_agree = true;
  376|    124|    }
  377|    124|    return ret;
  378|    124|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSF_NSt3__14spanIS6_Lm18446744073709551615EEE:
  338|    372|                                FromSpan src) const {
  339|    372|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    372|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE16calculate_lengthENSt3__14spanIS6_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    248|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    124|                                  const bool inputisvalid) const {
  383|    124|    conversion_result ret{};
  384|       |
  385|    124|    const auto implementations = get_supported_implementations();
  386|       |
  387|    124|    std::vector<result<ConversionResult>> results;
  388|    124|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    124|    std::vector<std::vector<ToType>> outputbuffers;
  393|    124|    outputbuffers.reserve(implementations.size());
  394|    496|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 372, False: 124]
  ------------------
  395|    372|      auto impl = implementations[i];
  396|    372|      const ToType canary1{42};
  397|    372|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    372|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    372|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    372|      const auto success = [](const ConversionResult& r) -> bool {
  402|    372|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    372|          return r != 0;
  404|    372|        } else {
  405|    372|          return r.error == simdutf::error_code::SUCCESS;
  406|    372|        }
  407|    372|      }(implret1);
  408|    372|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    372|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    372|        const ToType canary2{25};
  414|    372|        const auto outputbuffer_first_run = outputbuffer;
  415|    372|        std::ranges::fill(outputbuffer, canary2);
  416|    372|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    372|                                          src.size(), outputbuffer.data());
  418|       |
  419|    372|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 372]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    372|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 306, False: 66]
  |  Branch (423:29): [True: 129, False: 177]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    129|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    129|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 129]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    129|        }
  440|    372|      }
  441|    372|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 129, False: 243]
  ------------------
  442|    372|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    124|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 22, False: 102]
  ------------------
  447|     66|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 66, False: 22]
  ------------------
  448|     66|        e.outputhash.clear();
  449|     66|      }
  450|     22|    }
  451|       |
  452|    124|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    124|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 124]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    124|    } else {
  474|    124|      ret.implementations_agree = true;
  475|    124|    }
  476|    124|    return ret;
  477|    124|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS5_E_clESM_:
  401|    372|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|    372|        } else {
  405|    372|          return r.error == simdutf::error_code::SUCCESS;
  406|    372|        }
  407|    372|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS5_ESU_EEDaSN_SQ_:
  452|    248|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_25clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    180|    +[](std::span<const char> chardata) {                                      \
  555|    180|      const auto c =                                                           \
  556|    180|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    180|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    180|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    180|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    180|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    180|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    180|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    180|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    180|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    180|  constexpr auto _size = _name.size();                                \
  |  | 1183|    180|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    180|  return _nameof; }()
  ------------------
  561|    180|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    180|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    180|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    180|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    180|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    180|  constexpr auto _size = _name.size();                                \
  |  | 1183|    180|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    180|  return _nameof; }()
  ------------------
  562|    180|      c.fuzz(chardata);                                                        \
  563|    180|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    180|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    180|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    180|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    180|    static const bool do_print_testcase =
  181|    180|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    180|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 180]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    180|    do {
  189|       |      // step 0 - is the input valid?
  190|    180|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    180|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 180]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    180|                    From == UtfEncodings::UTF8) {
  198|    180|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 180]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    180|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    180|      const auto [output_length, length_agree] =
  204|    180|          calculate_length(from, inputisvalid);
  205|    180|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 180]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    180|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 100, False: 80]
  |  Branch (208:28): [True: 0, False: 100]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    180|      const auto [written, outputs_agree] =
  215|    180|          do_conversion(from, output_length, inputisvalid);
  216|    180|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 180]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    180|      return;
  221|    180|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    180|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    180|  validation_result verify_valid_input(FromSpan src) const {
  231|    180|    validation_result ret{};
  232|       |
  233|    180|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    180|    const auto implementations = get_supported_implementations();
  235|    180|    std::vector<simdutf::result> results;
  236|    180|    results.reserve(implementations.size());
  237|       |
  238|    540|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 540, False: 180]
  ------------------
  239|    540|      results.push_back(
  240|    540|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    540|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    540|      const bool validation2 =
  245|    540|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    540|                      src.data(), src.size());
  247|    540|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 540]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|    540|    }
  258|       |
  259|    180|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    180|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 180]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    180|    } else {
  273|    180|      ret.implementations_agree = true;
  274|    180|    }
  275|    180|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    180|      return r.error == simdutf::SUCCESS;
  277|    180|    });
  278|    180|    return ret;
  279|    180|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    360|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKS7_E_clESI_:
  275|    340|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    340|      return r.error == simdutf::SUCCESS;
  277|    340|    });
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    180|  bool count_the_input(FromSpan src) const {
  290|    180|    const auto implementations = get_supported_implementations();
  291|    180|    std::vector<std::size_t> results;
  292|    180|    results.reserve(implementations.size());
  293|       |
  294|    540|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 540, False: 180]
  ------------------
  295|    540|      std::size_t ret;
  296|    540|      if constexpr (From == UtfEncodings::UTF16BE) {
  297|    540|        ret = impl->count_utf16be(src.data(), src.size());
  298|       |      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|       |        ret = impl->count_utf16le(src.data(), src.size());
  300|       |      } else if constexpr (From == UtfEncodings::UTF8) {
  301|       |        ret = impl->count_utf8(src.data(), src.size());
  302|       |      }
  303|    540|      results.push_back(ret);
  304|    540|    }
  305|    180|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    180|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 180]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    180|    return true;
  321|    180|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    360|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    180|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    180|    length_result ret{};
  344|       |
  345|    180|    const auto implementations = get_supported_implementations();
  346|    180|    std::vector<std::size_t> results;
  347|    180|    results.reserve(implementations.size());
  348|       |
  349|    540|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 540, False: 180]
  ------------------
  350|    540|      const auto len = invoke_lengthcalc(impl, src);
  351|    540|      results.push_back(len);
  352|    540|      ret.length.push_back(len);
  353|    540|    }
  354|       |
  355|    180|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    180|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 180]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    180|    } else {
  375|    180|      ret.implementations_agree = true;
  376|    180|    }
  377|    180|    return ret;
  378|    180|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    540|                                FromSpan src) const {
  331|    540|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    540|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    360|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    180|                                  const bool inputisvalid) const {
  383|    180|    conversion_result ret{};
  384|       |
  385|    180|    const auto implementations = get_supported_implementations();
  386|       |
  387|    180|    std::vector<result<ConversionResult>> results;
  388|    180|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    180|    std::vector<std::vector<ToType>> outputbuffers;
  393|    180|    outputbuffers.reserve(implementations.size());
  394|    720|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 540, False: 180]
  ------------------
  395|    540|      auto impl = implementations[i];
  396|    540|      const ToType canary1{42};
  397|    540|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    540|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    540|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    540|      const auto success = [](const ConversionResult& r) -> bool {
  402|    540|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    540|          return r != 0;
  404|    540|        } else {
  405|    540|          return r.error == simdutf::error_code::SUCCESS;
  406|    540|        }
  407|    540|      }(implret1);
  408|    540|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    540|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    540|        const ToType canary2{25};
  414|    540|        const auto outputbuffer_first_run = outputbuffer;
  415|    540|        std::ranges::fill(outputbuffer, canary2);
  416|    540|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    540|                                          src.size(), outputbuffer.data());
  418|       |
  419|    540|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 540]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    540|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 240, False: 300]
  |  Branch (423:29): [True: 240, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    240|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    240|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 240]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    240|        }
  440|    540|      }
  441|    540|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 240, False: 300]
  ------------------
  442|    540|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    180|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 100, False: 80]
  ------------------
  447|    300|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 300, False: 100]
  ------------------
  448|    300|        e.outputhash.clear();
  449|    300|      }
  450|    100|    }
  451|       |
  452|    180|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    180|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 180]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    180|    } else {
  474|    180|      ret.implementations_agree = true;
  475|    180|    }
  476|    180|    return ret;
  477|    180|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|    540|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|    540|        } else {
  405|    540|          return r.error == simdutf::error_code::SUCCESS;
  406|    540|        }
  407|    540|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    360|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_26clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    278|    +[](std::span<const char> chardata) {                                      \
  555|    278|      const auto c =                                                           \
  556|    278|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    278|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    278|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    278|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    278|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    278|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    278|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    278|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    278|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    278|  constexpr auto _size = _name.size();                                \
  |  | 1183|    278|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    278|  return _nameof; }()
  ------------------
  561|    278|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    278|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    278|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    278|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    278|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    278|  constexpr auto _size = _name.size();                                \
  |  | 1183|    278|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    278|  return _nameof; }()
  ------------------
  562|    278|      c.fuzz(chardata);                                                        \
  563|    278|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    278|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    278|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    278|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    278|    static const bool do_print_testcase =
  181|    278|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    278|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 278]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    278|    do {
  189|       |      // step 0 - is the input valid?
  190|    278|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    278|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 278]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    278|                    From == UtfEncodings::UTF8) {
  198|    278|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 278]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    278|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    278|      const auto [output_length, length_agree] =
  204|    278|          calculate_length(from, inputisvalid);
  205|    278|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 278]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    278|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 117, False: 161]
  |  Branch (208:28): [True: 0, False: 117]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    278|      const auto [written, outputs_agree] =
  215|    278|          do_conversion(from, output_length, inputisvalid);
  216|    278|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 278]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    278|      return;
  221|    278|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    278|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    278|  validation_result verify_valid_input(FromSpan src) const {
  231|    278|    validation_result ret{};
  232|       |
  233|    278|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    278|    const auto implementations = get_supported_implementations();
  235|    278|    std::vector<simdutf::result> results;
  236|    278|    results.reserve(implementations.size());
  237|       |
  238|    834|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 834, False: 278]
  ------------------
  239|    834|      results.push_back(
  240|    834|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    834|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    834|      const bool validation2 =
  245|    834|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    834|                      src.data(), src.size());
  247|    834|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 834]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|    834|    }
  258|       |
  259|    278|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    278|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 278]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    278|    } else {
  273|    278|      ret.implementations_agree = true;
  274|    278|    }
  275|    278|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    278|      return r.error == simdutf::SUCCESS;
  277|    278|    });
  278|    278|    return ret;
  279|    278|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    556|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKS7_E_clESI_:
  275|    600|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    600|      return r.error == simdutf::SUCCESS;
  277|    600|    });
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    278|  bool count_the_input(FromSpan src) const {
  290|    278|    const auto implementations = get_supported_implementations();
  291|    278|    std::vector<std::size_t> results;
  292|    278|    results.reserve(implementations.size());
  293|       |
  294|    834|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 834, False: 278]
  ------------------
  295|    834|      std::size_t ret;
  296|    834|      if constexpr (From == UtfEncodings::UTF16BE) {
  297|    834|        ret = impl->count_utf16be(src.data(), src.size());
  298|       |      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|       |        ret = impl->count_utf16le(src.data(), src.size());
  300|       |      } else if constexpr (From == UtfEncodings::UTF8) {
  301|       |        ret = impl->count_utf8(src.data(), src.size());
  302|       |      }
  303|    834|      results.push_back(ret);
  304|    834|    }
  305|    278|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    278|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 278]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    278|    return true;
  321|    278|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    556|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    278|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    278|    length_result ret{};
  344|       |
  345|    278|    const auto implementations = get_supported_implementations();
  346|    278|    std::vector<std::size_t> results;
  347|    278|    results.reserve(implementations.size());
  348|       |
  349|    834|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 834, False: 278]
  ------------------
  350|    834|      const auto len = invoke_lengthcalc(impl, src);
  351|    834|      results.push_back(len);
  352|    834|      ret.length.push_back(len);
  353|    834|    }
  354|       |
  355|    278|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    278|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 278]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    278|    } else {
  375|    278|      ret.implementations_agree = true;
  376|    278|    }
  377|    278|    return ret;
  378|    278|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    834|                                FromSpan src) const {
  331|    834|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    834|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    556|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    278|                                  const bool inputisvalid) const {
  383|    278|    conversion_result ret{};
  384|       |
  385|    278|    const auto implementations = get_supported_implementations();
  386|       |
  387|    278|    std::vector<result<ConversionResult>> results;
  388|    278|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    278|    std::vector<std::vector<ToType>> outputbuffers;
  393|    278|    outputbuffers.reserve(implementations.size());
  394|  1.11k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 834, False: 278]
  ------------------
  395|    834|      auto impl = implementations[i];
  396|    834|      const ToType canary1{42};
  397|    834|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    834|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    834|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    834|      const auto success = [](const ConversionResult& r) -> bool {
  402|    834|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    834|          return r != 0;
  404|    834|        } else {
  405|    834|          return r.error == simdutf::error_code::SUCCESS;
  406|    834|        }
  407|    834|      }(implret1);
  408|    834|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    834|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    834|        const ToType canary2{25};
  414|    834|        const auto outputbuffer_first_run = outputbuffer;
  415|    834|        std::ranges::fill(outputbuffer, canary2);
  416|    834|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    834|                                          src.size(), outputbuffer.data());
  418|       |
  419|    834|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 834]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    834|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 483, False: 351]
  |  Branch (423:29): [True: 483, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    483|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    483|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 483]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    483|        }
  440|    834|      }
  441|    834|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 483, False: 351]
  ------------------
  442|    834|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    278|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 117, False: 161]
  ------------------
  447|    351|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 351, False: 117]
  ------------------
  448|    351|        e.outputhash.clear();
  449|    351|      }
  450|    117|    }
  451|       |
  452|    278|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    278|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 278]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    278|    } else {
  474|    278|      ret.implementations_agree = true;
  475|    278|    }
  476|    278|    return ret;
  477|    278|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|    834|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|    834|        } else {
  405|    834|          return r.error == simdutf::error_code::SUCCESS;
  406|    834|        }
  407|    834|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    556|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_27clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    140|    +[](std::span<const char> chardata) {                                      \
  555|    140|      const auto c =                                                           \
  556|    140|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    140|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    140|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    140|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    140|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    140|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    140|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    140|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    140|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    140|  constexpr auto _size = _name.size();                                \
  |  | 1183|    140|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    140|  return _nameof; }()
  ------------------
  561|    140|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    140|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    140|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    140|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    140|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    140|  constexpr auto _size = _name.size();                                \
  |  | 1183|    140|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    140|  return _nameof; }()
  ------------------
  562|    140|      c.fuzz(chardata);                                                        \
  563|    140|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    140|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    140|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    140|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    140|    static const bool do_print_testcase =
  181|    140|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    140|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 140]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    140|    do {
  189|       |      // step 0 - is the input valid?
  190|    140|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    140|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 140]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    140|                    From == UtfEncodings::UTF8) {
  198|    140|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 140]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    140|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    140|      const auto [output_length, length_agree] =
  204|    140|          calculate_length(from, inputisvalid);
  205|    140|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 140]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    140|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 22, False: 118]
  |  Branch (208:28): [True: 0, False: 22]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    140|      const auto [written, outputs_agree] =
  215|    140|          do_conversion(from, output_length, inputisvalid);
  216|    140|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 140]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    140|      return;
  221|    140|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    140|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS6_Lm18446744073709551615EEE:
  230|    140|  validation_result verify_valid_input(FromSpan src) const {
  231|    140|    validation_result ret{};
  232|       |
  233|    140|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    140|    const auto implementations = get_supported_implementations();
  235|    140|    std::vector<simdutf::result> results;
  236|    140|    results.reserve(implementations.size());
  237|       |
  238|    420|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 420, False: 140]
  ------------------
  239|    420|      results.push_back(
  240|    420|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    420|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    420|      const bool validation2 =
  245|    420|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    420|                      src.data(), src.size());
  247|    420|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 420]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|    420|    }
  258|       |
  259|    140|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    140|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 140]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    140|    } else {
  273|    140|      ret.implementations_agree = true;
  274|    140|    }
  275|    140|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    140|      return r.error == simdutf::SUCCESS;
  277|    140|    });
  278|    140|    return ret;
  279|    140|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS6_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS5_S5_EEDaSJ_SM_:
  259|    280|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS6_Lm18446744073709551615EEEENKUlRKS5_E_clESI_:
  275|    376|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    376|      return r.error == simdutf::SUCCESS;
  277|    376|    });
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE15count_the_inputENSt3__14spanIS6_Lm18446744073709551615EEE:
  289|    140|  bool count_the_input(FromSpan src) const {
  290|    140|    const auto implementations = get_supported_implementations();
  291|    140|    std::vector<std::size_t> results;
  292|    140|    results.reserve(implementations.size());
  293|       |
  294|    420|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 420, False: 140]
  ------------------
  295|    420|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|    420|      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|    420|        ret = impl->count_utf16le(src.data(), src.size());
  300|       |      } else if constexpr (From == UtfEncodings::UTF8) {
  301|       |        ret = impl->count_utf8(src.data(), src.size());
  302|       |      }
  303|    420|      results.push_back(ret);
  304|    420|    }
  305|    140|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    140|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 140]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    140|    return true;
  321|    140|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE15count_the_inputENSt3__14spanIS6_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    280|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE16calculate_lengthENSt3__14spanIS6_Lm18446744073709551615EEEb:
  342|    140|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    140|    length_result ret{};
  344|       |
  345|    140|    const auto implementations = get_supported_implementations();
  346|    140|    std::vector<std::size_t> results;
  347|    140|    results.reserve(implementations.size());
  348|       |
  349|    420|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 420, False: 140]
  ------------------
  350|    420|      const auto len = invoke_lengthcalc(impl, src);
  351|    420|      results.push_back(len);
  352|    420|      ret.length.push_back(len);
  353|    420|    }
  354|       |
  355|    140|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    140|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 140]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    140|    } else {
  375|    140|      ret.implementations_agree = true;
  376|    140|    }
  377|    140|    return ret;
  378|    140|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSF_NSt3__14spanIS6_Lm18446744073709551615EEE:
  338|    420|                                FromSpan src) const {
  339|    420|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    420|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE16calculate_lengthENSt3__14spanIS6_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    280|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    140|                                  const bool inputisvalid) const {
  383|    140|    conversion_result ret{};
  384|       |
  385|    140|    const auto implementations = get_supported_implementations();
  386|       |
  387|    140|    std::vector<result<ConversionResult>> results;
  388|    140|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    140|    std::vector<std::vector<ToType>> outputbuffers;
  393|    140|    outputbuffers.reserve(implementations.size());
  394|    560|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 420, False: 140]
  ------------------
  395|    420|      auto impl = implementations[i];
  396|    420|      const ToType canary1{42};
  397|    420|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    420|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    420|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    420|      const auto success = [](const ConversionResult& r) -> bool {
  402|    420|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    420|          return r != 0;
  404|    420|        } else {
  405|    420|          return r.error == simdutf::error_code::SUCCESS;
  406|    420|        }
  407|    420|      }(implret1);
  408|    420|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    420|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    420|        const ToType canary2{25};
  414|    420|        const auto outputbuffer_first_run = outputbuffer;
  415|    420|        std::ranges::fill(outputbuffer, canary2);
  416|    420|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    420|                                          src.size(), outputbuffer.data());
  418|       |
  419|    420|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 420]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    420|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 354, False: 66]
  |  Branch (423:29): [True: 159, False: 195]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    159|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    159|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 159]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    159|        }
  440|    420|      }
  441|    420|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 159, False: 261]
  ------------------
  442|    420|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    140|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 22, False: 118]
  ------------------
  447|     66|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 66, False: 22]
  ------------------
  448|     66|        e.outputhash.clear();
  449|     66|      }
  450|     22|    }
  451|       |
  452|    140|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    140|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 140]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    140|    } else {
  474|    140|      ret.implementations_agree = true;
  475|    140|    }
  476|    140|    return ret;
  477|    140|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS5_E_clESM_:
  401|    420|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|    420|        } else {
  405|    420|          return r.error == simdutf::error_code::SUCCESS;
  406|    420|        }
  407|    420|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS5_ESU_EEDaSN_SQ_:
  452|    280|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_28clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    221|    +[](std::span<const char> chardata) {                                      \
  555|    221|      const auto c =                                                           \
  556|    221|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    221|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    221|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    221|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    221|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    221|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    221|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    221|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    221|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    221|  constexpr auto _size = _name.size();                                \
  |  | 1183|    221|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    221|  return _nameof; }()
  ------------------
  561|    221|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    221|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    221|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    221|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    221|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    221|  constexpr auto _size = _name.size();                                \
  |  | 1183|    221|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    221|  return _nameof; }()
  ------------------
  562|    221|      c.fuzz(chardata);                                                        \
  563|    221|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    221|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    221|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    221|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    221|    static const bool do_print_testcase =
  181|    221|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    221|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 221]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    221|    do {
  189|       |      // step 0 - is the input valid?
  190|    221|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    221|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 221]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    221|                    From == UtfEncodings::UTF8) {
  198|    221|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 221]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    221|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    221|      const auto [output_length, length_agree] =
  204|    221|          calculate_length(from, inputisvalid);
  205|    221|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 221]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    221|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 120, False: 101]
  |  Branch (208:28): [True: 0, False: 120]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    221|      const auto [written, outputs_agree] =
  215|    221|          do_conversion(from, output_length, inputisvalid);
  216|    221|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 221]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    221|      return;
  221|    221|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    221|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    221|  validation_result verify_valid_input(FromSpan src) const {
  231|    221|    validation_result ret{};
  232|       |
  233|    221|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    221|    const auto implementations = get_supported_implementations();
  235|    221|    std::vector<simdutf::result> results;
  236|    221|    results.reserve(implementations.size());
  237|       |
  238|    663|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 663, False: 221]
  ------------------
  239|    663|      results.push_back(
  240|    663|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    663|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    663|      const bool validation2 =
  245|    663|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    663|                      src.data(), src.size());
  247|    663|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 663]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|    663|    }
  258|       |
  259|    221|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    221|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 221]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    221|    } else {
  273|    221|      ret.implementations_agree = true;
  274|    221|    }
  275|    221|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    221|      return r.error == simdutf::SUCCESS;
  277|    221|    });
  278|    221|    return ret;
  279|    221|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    442|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKS7_E_clESI_:
  275|    423|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    423|      return r.error == simdutf::SUCCESS;
  277|    423|    });
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    221|  bool count_the_input(FromSpan src) const {
  290|    221|    const auto implementations = get_supported_implementations();
  291|    221|    std::vector<std::size_t> results;
  292|    221|    results.reserve(implementations.size());
  293|       |
  294|    663|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 663, False: 221]
  ------------------
  295|    663|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|    663|      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|    663|        ret = impl->count_utf16le(src.data(), src.size());
  300|       |      } else if constexpr (From == UtfEncodings::UTF8) {
  301|       |        ret = impl->count_utf8(src.data(), src.size());
  302|       |      }
  303|    663|      results.push_back(ret);
  304|    663|    }
  305|    221|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    221|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 221]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    221|    return true;
  321|    221|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    442|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    221|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    221|    length_result ret{};
  344|       |
  345|    221|    const auto implementations = get_supported_implementations();
  346|    221|    std::vector<std::size_t> results;
  347|    221|    results.reserve(implementations.size());
  348|       |
  349|    663|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 663, False: 221]
  ------------------
  350|    663|      const auto len = invoke_lengthcalc(impl, src);
  351|    663|      results.push_back(len);
  352|    663|      ret.length.push_back(len);
  353|    663|    }
  354|       |
  355|    221|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    221|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 221]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    221|    } else {
  375|    221|      ret.implementations_agree = true;
  376|    221|    }
  377|    221|    return ret;
  378|    221|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    663|                                FromSpan src) const {
  331|    663|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    663|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    442|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    221|                                  const bool inputisvalid) const {
  383|    221|    conversion_result ret{};
  384|       |
  385|    221|    const auto implementations = get_supported_implementations();
  386|       |
  387|    221|    std::vector<result<ConversionResult>> results;
  388|    221|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    221|    std::vector<std::vector<ToType>> outputbuffers;
  393|    221|    outputbuffers.reserve(implementations.size());
  394|    884|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 663, False: 221]
  ------------------
  395|    663|      auto impl = implementations[i];
  396|    663|      const ToType canary1{42};
  397|    663|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    663|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    663|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    663|      const auto success = [](const ConversionResult& r) -> bool {
  402|    663|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    663|          return r != 0;
  404|    663|        } else {
  405|    663|          return r.error == simdutf::error_code::SUCCESS;
  406|    663|        }
  407|    663|      }(implret1);
  408|    663|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    663|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    663|        const ToType canary2{25};
  414|    663|        const auto outputbuffer_first_run = outputbuffer;
  415|    663|        std::ranges::fill(outputbuffer, canary2);
  416|    663|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    663|                                          src.size(), outputbuffer.data());
  418|       |
  419|    663|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 663]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    663|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 303, False: 360]
  |  Branch (423:29): [True: 303, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    303|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    303|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 303]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    303|        }
  440|    663|      }
  441|    663|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 303, False: 360]
  ------------------
  442|    663|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    221|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 120, False: 101]
  ------------------
  447|    360|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 360, False: 120]
  ------------------
  448|    360|        e.outputhash.clear();
  449|    360|      }
  450|    120|    }
  451|       |
  452|    221|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    221|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 221]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    221|    } else {
  474|    221|      ret.implementations_agree = true;
  475|    221|    }
  476|    221|    return ret;
  477|    221|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|    663|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|    663|        } else {
  405|    663|          return r.error == simdutf::error_code::SUCCESS;
  406|    663|        }
  407|    663|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    442|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_29clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    343|    +[](std::span<const char> chardata) {                                      \
  555|    343|      const auto c =                                                           \
  556|    343|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    343|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    343|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    343|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    343|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    343|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    343|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    343|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    343|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    343|  constexpr auto _size = _name.size();                                \
  |  | 1183|    343|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    343|  return _nameof; }()
  ------------------
  561|    343|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    343|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    343|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    343|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    343|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    343|  constexpr auto _size = _name.size();                                \
  |  | 1183|    343|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    343|  return _nameof; }()
  ------------------
  562|    343|      c.fuzz(chardata);                                                        \
  563|    343|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    343|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    343|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    343|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    343|    static const bool do_print_testcase =
  181|    343|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    343|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 343]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    343|    do {
  189|       |      // step 0 - is the input valid?
  190|    343|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    343|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 343]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    343|                    From == UtfEncodings::UTF8) {
  198|    343|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 343]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    343|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    343|      const auto [output_length, length_agree] =
  204|    343|          calculate_length(from, inputisvalid);
  205|    343|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 343]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    343|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 113, False: 230]
  |  Branch (208:28): [True: 0, False: 113]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    343|      const auto [written, outputs_agree] =
  215|    343|          do_conversion(from, output_length, inputisvalid);
  216|    343|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 343]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    343|      return;
  221|    343|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    343|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    343|  validation_result verify_valid_input(FromSpan src) const {
  231|    343|    validation_result ret{};
  232|       |
  233|    343|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    343|    const auto implementations = get_supported_implementations();
  235|    343|    std::vector<simdutf::result> results;
  236|    343|    results.reserve(implementations.size());
  237|       |
  238|  1.02k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.02k, False: 343]
  ------------------
  239|  1.02k|      results.push_back(
  240|  1.02k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.02k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.02k|      const bool validation2 =
  245|  1.02k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.02k|                      src.data(), src.size());
  247|  1.02k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.02k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  1.02k|    }
  258|       |
  259|    343|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    343|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 343]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    343|    } else {
  273|    343|      ret.implementations_agree = true;
  274|    343|    }
  275|    343|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    343|      return r.error == simdutf::SUCCESS;
  277|    343|    });
  278|    343|    return ret;
  279|    343|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    686|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKS7_E_clESI_:
  275|    803|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    803|      return r.error == simdutf::SUCCESS;
  277|    803|    });
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    343|  bool count_the_input(FromSpan src) const {
  290|    343|    const auto implementations = get_supported_implementations();
  291|    343|    std::vector<std::size_t> results;
  292|    343|    results.reserve(implementations.size());
  293|       |
  294|  1.02k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.02k, False: 343]
  ------------------
  295|  1.02k|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|  1.02k|      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|  1.02k|        ret = impl->count_utf16le(src.data(), src.size());
  300|       |      } else if constexpr (From == UtfEncodings::UTF8) {
  301|       |        ret = impl->count_utf8(src.data(), src.size());
  302|       |      }
  303|  1.02k|      results.push_back(ret);
  304|  1.02k|    }
  305|    343|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    343|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 343]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    343|    return true;
  321|    343|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    686|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    343|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    343|    length_result ret{};
  344|       |
  345|    343|    const auto implementations = get_supported_implementations();
  346|    343|    std::vector<std::size_t> results;
  347|    343|    results.reserve(implementations.size());
  348|       |
  349|  1.02k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.02k, False: 343]
  ------------------
  350|  1.02k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.02k|      results.push_back(len);
  352|  1.02k|      ret.length.push_back(len);
  353|  1.02k|    }
  354|       |
  355|    343|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    343|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 343]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    343|    } else {
  375|    343|      ret.implementations_agree = true;
  376|    343|    }
  377|    343|    return ret;
  378|    343|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.02k|                                FromSpan src) const {
  331|  1.02k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.02k|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    686|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    343|                                  const bool inputisvalid) const {
  383|    343|    conversion_result ret{};
  384|       |
  385|    343|    const auto implementations = get_supported_implementations();
  386|       |
  387|    343|    std::vector<result<ConversionResult>> results;
  388|    343|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    343|    std::vector<std::vector<ToType>> outputbuffers;
  393|    343|    outputbuffers.reserve(implementations.size());
  394|  1.37k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.02k, False: 343]
  ------------------
  395|  1.02k|      auto impl = implementations[i];
  396|  1.02k|      const ToType canary1{42};
  397|  1.02k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.02k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.02k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.02k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.02k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.02k|          return r != 0;
  404|  1.02k|        } else {
  405|  1.02k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.02k|        }
  407|  1.02k|      }(implret1);
  408|  1.02k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.02k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  1.02k|        const ToType canary2{25};
  414|  1.02k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.02k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.02k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.02k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.02k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.02k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.02k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 690, False: 339]
  |  Branch (423:29): [True: 690, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    690|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    690|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 690]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    690|        }
  440|  1.02k|      }
  441|  1.02k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 690, False: 339]
  ------------------
  442|  1.02k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    343|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 113, False: 230]
  ------------------
  447|    339|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 339, False: 113]
  ------------------
  448|    339|        e.outputhash.clear();
  449|    339|      }
  450|    113|    }
  451|       |
  452|    343|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    343|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 343]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    343|    } else {
  474|    343|      ret.implementations_agree = true;
  475|    343|    }
  476|    343|    return ret;
  477|    343|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|  1.02k|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|  1.02k|        } else {
  405|  1.02k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.02k|        }
  407|  1.02k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    686|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_30clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    210|    +[](std::span<const char> chardata) {                                      \
  555|    210|      const auto c =                                                           \
  556|    210|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    210|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    210|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    210|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    210|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    210|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    210|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    210|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    210|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    210|  constexpr auto _size = _name.size();                                \
  |  | 1183|    210|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    210|  return _nameof; }()
  ------------------
  561|    210|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    210|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    210|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    210|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    210|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    210|  constexpr auto _size = _name.size();                                \
  |  | 1183|    210|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    210|  return _nameof; }()
  ------------------
  562|    210|      c.fuzz(chardata);                                                        \
  563|    210|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    210|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    210|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    210|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    210|    static const bool do_print_testcase =
  181|    210|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    210|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 210]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    210|    do {
  189|       |      // step 0 - is the input valid?
  190|    210|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    210|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 210]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|       |                    From == UtfEncodings::UTF8) {
  198|       |        if (!count_the_input(from) && !allow_implementations_to_differ)
  199|       |          break;
  200|       |      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    210|      const auto [output_length, length_agree] =
  204|    210|          calculate_length(from, inputisvalid);
  205|    210|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 210]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    210|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 179, False: 31]
  |  Branch (208:28): [True: 0, False: 179]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    210|      const auto [written, outputs_agree] =
  215|    210|          do_conversion(from, output_length, inputisvalid);
  216|    210|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 210]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    210|      return;
  221|    210|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    210|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS6_Lm18446744073709551615EEE:
  230|    210|  validation_result verify_valid_input(FromSpan src) const {
  231|    210|    validation_result ret{};
  232|       |
  233|    210|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    210|    const auto implementations = get_supported_implementations();
  235|    210|    std::vector<simdutf::result> results;
  236|    210|    results.reserve(implementations.size());
  237|       |
  238|    630|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 630, False: 210]
  ------------------
  239|    630|      results.push_back(
  240|    630|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    630|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    630|      const bool validation2 =
  245|    630|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    630|                      src.data(), src.size());
  247|    630|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 630]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|    630|    }
  258|       |
  259|    210|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    210|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 210]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    210|    } else {
  273|    210|      ret.implementations_agree = true;
  274|    210|    }
  275|    210|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    210|      return r.error == simdutf::SUCCESS;
  277|    210|    });
  278|    210|    return ret;
  279|    210|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS6_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS5_S5_EEDaSJ_SM_:
  259|    420|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS6_Lm18446744073709551615EEEENKUlRKS5_E_clESI_:
  275|    272|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    272|      return r.error == simdutf::SUCCESS;
  277|    272|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE16calculate_lengthENSt3__14spanIS6_Lm18446744073709551615EEEb:
  342|    210|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    210|    length_result ret{};
  344|       |
  345|    210|    const auto implementations = get_supported_implementations();
  346|    210|    std::vector<std::size_t> results;
  347|    210|    results.reserve(implementations.size());
  348|       |
  349|    630|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 630, False: 210]
  ------------------
  350|    630|      const auto len = invoke_lengthcalc(impl, src);
  351|    630|      results.push_back(len);
  352|    630|      ret.length.push_back(len);
  353|    630|    }
  354|       |
  355|    210|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    210|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 210]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    210|    } else {
  375|    210|      ret.implementations_agree = true;
  376|    210|    }
  377|    210|    return ret;
  378|    210|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSF_NSt3__14spanIS6_Lm18446744073709551615EEE:
  338|    630|                                FromSpan src) const {
  339|    630|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    630|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE16calculate_lengthENSt3__14spanIS6_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    420|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    210|                                  const bool inputisvalid) const {
  383|    210|    conversion_result ret{};
  384|       |
  385|    210|    const auto implementations = get_supported_implementations();
  386|       |
  387|    210|    std::vector<result<ConversionResult>> results;
  388|    210|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    210|    std::vector<std::vector<ToType>> outputbuffers;
  393|    210|    outputbuffers.reserve(implementations.size());
  394|    840|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 630, False: 210]
  ------------------
  395|    630|      auto impl = implementations[i];
  396|    630|      const ToType canary1{42};
  397|    630|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    630|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    630|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    630|      const auto success = [](const ConversionResult& r) -> bool {
  402|    630|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    630|          return r != 0;
  404|    630|        } else {
  405|    630|          return r.error == simdutf::error_code::SUCCESS;
  406|    630|        }
  407|    630|      }(implret1);
  408|    630|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    630|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    630|        const ToType canary2{25};
  414|    630|        const auto outputbuffer_first_run = outputbuffer;
  415|    630|        std::ranges::fill(outputbuffer, canary2);
  416|    630|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    630|                                          src.size(), outputbuffer.data());
  418|       |
  419|    630|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 630]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    630|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 93, False: 537]
  |  Branch (423:29): [True: 66, False: 27]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|     66|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|     66|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 66]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|     66|        }
  440|    630|      }
  441|    630|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 66, False: 564]
  ------------------
  442|    630|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    210|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 179, False: 31]
  ------------------
  447|    537|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 537, False: 179]
  ------------------
  448|    537|        e.outputhash.clear();
  449|    537|      }
  450|    179|    }
  451|       |
  452|    210|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    210|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 210]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    210|    } else {
  474|    210|      ret.implementations_agree = true;
  475|    210|    }
  476|    210|    return ret;
  477|    210|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS5_E_clESM_:
  401|    630|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|    630|        } else {
  405|    630|          return r.error == simdutf::error_code::SUCCESS;
  406|    630|        }
  407|    630|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS5_ESU_EEDaSN_SQ_:
  452|    420|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_31clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    335|    +[](std::span<const char> chardata) {                                      \
  555|    335|      const auto c =                                                           \
  556|    335|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    335|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    335|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    335|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    335|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    335|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    335|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    335|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    335|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    335|  constexpr auto _size = _name.size();                                \
  |  | 1183|    335|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    335|  return _nameof; }()
  ------------------
  561|    335|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    335|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    335|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    335|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    335|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    335|  constexpr auto _size = _name.size();                                \
  |  | 1183|    335|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    335|  return _nameof; }()
  ------------------
  562|    335|      c.fuzz(chardata);                                                        \
  563|    335|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    335|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    335|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    335|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    335|    static const bool do_print_testcase =
  181|    335|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    335|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 335]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    335|    do {
  189|       |      // step 0 - is the input valid?
  190|    335|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    335|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 335]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|       |                    From == UtfEncodings::UTF8) {
  198|       |        if (!count_the_input(from) && !allow_implementations_to_differ)
  199|       |          break;
  200|       |      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    335|      const auto [output_length, length_agree] =
  204|    335|          calculate_length(from, inputisvalid);
  205|    335|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 335]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    335|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 252, False: 83]
  |  Branch (208:28): [True: 0, False: 252]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    335|      const auto [written, outputs_agree] =
  215|    335|          do_conversion(from, output_length, inputisvalid);
  216|    335|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 335]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    335|      return;
  221|    335|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    335|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    335|  validation_result verify_valid_input(FromSpan src) const {
  231|    335|    validation_result ret{};
  232|       |
  233|    335|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    335|    const auto implementations = get_supported_implementations();
  235|    335|    std::vector<simdutf::result> results;
  236|    335|    results.reserve(implementations.size());
  237|       |
  238|  1.00k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.00k, False: 335]
  ------------------
  239|  1.00k|      results.push_back(
  240|  1.00k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.00k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.00k|      const bool validation2 =
  245|  1.00k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.00k|                      src.data(), src.size());
  247|  1.00k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.00k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  1.00k|    }
  258|       |
  259|    335|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    335|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 335]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    335|    } else {
  273|    335|      ret.implementations_agree = true;
  274|    335|    }
  275|    335|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    335|      return r.error == simdutf::SUCCESS;
  277|    335|    });
  278|    335|    return ret;
  279|    335|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    670|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKS7_E_clESI_:
  275|    501|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    501|      return r.error == simdutf::SUCCESS;
  277|    501|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    335|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    335|    length_result ret{};
  344|       |
  345|    335|    const auto implementations = get_supported_implementations();
  346|    335|    std::vector<std::size_t> results;
  347|    335|    results.reserve(implementations.size());
  348|       |
  349|  1.00k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.00k, False: 335]
  ------------------
  350|  1.00k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.00k|      results.push_back(len);
  352|  1.00k|      ret.length.push_back(len);
  353|  1.00k|    }
  354|       |
  355|    335|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    335|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 335]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    335|    } else {
  375|    335|      ret.implementations_agree = true;
  376|    335|    }
  377|    335|    return ret;
  378|    335|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.00k|                                FromSpan src) const {
  331|  1.00k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.00k|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    670|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    335|                                  const bool inputisvalid) const {
  383|    335|    conversion_result ret{};
  384|       |
  385|    335|    const auto implementations = get_supported_implementations();
  386|       |
  387|    335|    std::vector<result<ConversionResult>> results;
  388|    335|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    335|    std::vector<std::vector<ToType>> outputbuffers;
  393|    335|    outputbuffers.reserve(implementations.size());
  394|  1.34k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.00k, False: 335]
  ------------------
  395|  1.00k|      auto impl = implementations[i];
  396|  1.00k|      const ToType canary1{42};
  397|  1.00k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.00k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.00k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.00k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.00k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.00k|          return r != 0;
  404|  1.00k|        } else {
  405|  1.00k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.00k|        }
  407|  1.00k|      }(implret1);
  408|  1.00k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.00k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  1.00k|        const ToType canary2{25};
  414|  1.00k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.00k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.00k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.00k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.00k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.00k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.00k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 249, False: 756]
  |  Branch (423:29): [True: 249, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    249|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    249|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 249]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    249|        }
  440|  1.00k|      }
  441|  1.00k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 249, False: 756]
  ------------------
  442|  1.00k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    335|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 252, False: 83]
  ------------------
  447|    756|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 756, False: 252]
  ------------------
  448|    756|        e.outputhash.clear();
  449|    756|      }
  450|    252|    }
  451|       |
  452|    335|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    335|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 335]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    335|    } else {
  474|    335|      ret.implementations_agree = true;
  475|    335|    }
  476|    335|    return ret;
  477|    335|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|  1.00k|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|  1.00k|        } else {
  405|  1.00k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.00k|        }
  407|  1.00k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    670|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_32clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    374|    +[](std::span<const char> chardata) {                                      \
  555|    374|      const auto c =                                                           \
  556|    374|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    374|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    374|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    374|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    374|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    374|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    374|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    374|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    374|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    374|  constexpr auto _size = _name.size();                                \
  |  | 1183|    374|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    374|  return _nameof; }()
  ------------------
  561|    374|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    374|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    374|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    374|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    374|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    374|  constexpr auto _size = _name.size();                                \
  |  | 1183|    374|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    374|  return _nameof; }()
  ------------------
  562|    374|      c.fuzz(chardata);                                                        \
  563|    374|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    374|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    374|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    374|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    374|    static const bool do_print_testcase =
  181|    374|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    374|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 374]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    374|    do {
  189|       |      // step 0 - is the input valid?
  190|    374|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    374|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 374]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|       |                    From == UtfEncodings::UTF8) {
  198|       |        if (!count_the_input(from) && !allow_implementations_to_differ)
  199|       |          break;
  200|       |      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    374|      const auto [output_length, length_agree] =
  204|    374|          calculate_length(from, inputisvalid);
  205|    374|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 374]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    374|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 284, False: 90]
  |  Branch (208:28): [True: 0, False: 284]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    374|      const auto [written, outputs_agree] =
  215|    374|          do_conversion(from, output_length, inputisvalid);
  216|    374|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 374]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    374|      return;
  221|    374|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    374|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    374|  validation_result verify_valid_input(FromSpan src) const {
  231|    374|    validation_result ret{};
  232|       |
  233|    374|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    374|    const auto implementations = get_supported_implementations();
  235|    374|    std::vector<simdutf::result> results;
  236|    374|    results.reserve(implementations.size());
  237|       |
  238|  1.12k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.12k, False: 374]
  ------------------
  239|  1.12k|      results.push_back(
  240|  1.12k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.12k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.12k|      const bool validation2 =
  245|  1.12k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.12k|                      src.data(), src.size());
  247|  1.12k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.12k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  1.12k|    }
  258|       |
  259|    374|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    374|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 374]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    374|    } else {
  273|    374|      ret.implementations_agree = true;
  274|    374|    }
  275|    374|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    374|      return r.error == simdutf::SUCCESS;
  277|    374|    });
  278|    374|    return ret;
  279|    374|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    748|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKS7_E_clESI_:
  275|    554|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    554|      return r.error == simdutf::SUCCESS;
  277|    554|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    374|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    374|    length_result ret{};
  344|       |
  345|    374|    const auto implementations = get_supported_implementations();
  346|    374|    std::vector<std::size_t> results;
  347|    374|    results.reserve(implementations.size());
  348|       |
  349|  1.12k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.12k, False: 374]
  ------------------
  350|  1.12k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.12k|      results.push_back(len);
  352|  1.12k|      ret.length.push_back(len);
  353|  1.12k|    }
  354|       |
  355|    374|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    374|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 374]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    374|    } else {
  375|    374|      ret.implementations_agree = true;
  376|    374|    }
  377|    374|    return ret;
  378|    374|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.12k|                                FromSpan src) const {
  331|  1.12k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.12k|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    748|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    374|                                  const bool inputisvalid) const {
  383|    374|    conversion_result ret{};
  384|       |
  385|    374|    const auto implementations = get_supported_implementations();
  386|       |
  387|    374|    std::vector<result<ConversionResult>> results;
  388|    374|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    374|    std::vector<std::vector<ToType>> outputbuffers;
  393|    374|    outputbuffers.reserve(implementations.size());
  394|  1.49k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.12k, False: 374]
  ------------------
  395|  1.12k|      auto impl = implementations[i];
  396|  1.12k|      const ToType canary1{42};
  397|  1.12k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.12k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.12k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.12k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.12k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.12k|          return r != 0;
  404|  1.12k|        } else {
  405|  1.12k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.12k|        }
  407|  1.12k|      }(implret1);
  408|  1.12k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.12k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  1.12k|        const ToType canary2{25};
  414|  1.12k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.12k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.12k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.12k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.12k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.12k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.12k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 270, False: 852]
  |  Branch (423:29): [True: 270, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    270|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    270|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 270]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    270|        }
  440|  1.12k|      }
  441|  1.12k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 270, False: 852]
  ------------------
  442|  1.12k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    374|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 284, False: 90]
  ------------------
  447|    852|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 852, False: 284]
  ------------------
  448|    852|        e.outputhash.clear();
  449|    852|      }
  450|    284|    }
  451|       |
  452|    374|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    374|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 374]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    374|    } else {
  474|    374|      ret.implementations_agree = true;
  475|    374|    }
  476|    374|    return ret;
  477|    374|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|  1.12k|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|  1.12k|        } else {
  405|  1.12k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.12k|        }
  407|  1.12k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    748|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_33clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    432|    +[](std::span<const char> chardata) {                                      \
  555|    432|      const auto c =                                                           \
  556|    432|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    432|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    432|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    432|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    432|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    432|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    432|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    432|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    432|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    432|  constexpr auto _size = _name.size();                                \
  |  | 1183|    432|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    432|  return _nameof; }()
  ------------------
  561|    432|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    432|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    432|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    432|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    432|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    432|  constexpr auto _size = _name.size();                                \
  |  | 1183|    432|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    432|  return _nameof; }()
  ------------------
  562|    432|      c.fuzz(chardata);                                                        \
  563|    432|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    432|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    432|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    432|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    432|    static const bool do_print_testcase =
  181|    432|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    432|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 432]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    432|    do {
  189|       |      // step 0 - is the input valid?
  190|    432|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    432|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 432]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|       |                    From == UtfEncodings::UTF8) {
  198|       |        if (!count_the_input(from) && !allow_implementations_to_differ)
  199|       |          break;
  200|       |      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    432|      const auto [output_length, length_agree] =
  204|    432|          calculate_length(from, inputisvalid);
  205|    432|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 432]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    432|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 328, False: 104]
  |  Branch (208:28): [True: 0, False: 328]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    432|      const auto [written, outputs_agree] =
  215|    432|          do_conversion(from, output_length, inputisvalid);
  216|    432|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 432]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    432|      return;
  221|    432|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    432|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    432|  validation_result verify_valid_input(FromSpan src) const {
  231|    432|    validation_result ret{};
  232|       |
  233|    432|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    432|    const auto implementations = get_supported_implementations();
  235|    432|    std::vector<simdutf::result> results;
  236|    432|    results.reserve(implementations.size());
  237|       |
  238|  1.29k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.29k, False: 432]
  ------------------
  239|  1.29k|      results.push_back(
  240|  1.29k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.29k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.29k|      const bool validation2 =
  245|  1.29k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.29k|                      src.data(), src.size());
  247|  1.29k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.29k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  1.29k|    }
  258|       |
  259|    432|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    432|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 432]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    432|    } else {
  273|    432|      ret.implementations_agree = true;
  274|    432|    }
  275|    432|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    432|      return r.error == simdutf::SUCCESS;
  277|    432|    });
  278|    432|    return ret;
  279|    432|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    864|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKS7_E_clESI_:
  275|    640|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    640|      return r.error == simdutf::SUCCESS;
  277|    640|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    432|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    432|    length_result ret{};
  344|       |
  345|    432|    const auto implementations = get_supported_implementations();
  346|    432|    std::vector<std::size_t> results;
  347|    432|    results.reserve(implementations.size());
  348|       |
  349|  1.29k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.29k, False: 432]
  ------------------
  350|  1.29k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.29k|      results.push_back(len);
  352|  1.29k|      ret.length.push_back(len);
  353|  1.29k|    }
  354|       |
  355|    432|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    432|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 432]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    432|    } else {
  375|    432|      ret.implementations_agree = true;
  376|    432|    }
  377|    432|    return ret;
  378|    432|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.29k|                                FromSpan src) const {
  331|  1.29k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.29k|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    864|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    432|                                  const bool inputisvalid) const {
  383|    432|    conversion_result ret{};
  384|       |
  385|    432|    const auto implementations = get_supported_implementations();
  386|       |
  387|    432|    std::vector<result<ConversionResult>> results;
  388|    432|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    432|    std::vector<std::vector<ToType>> outputbuffers;
  393|    432|    outputbuffers.reserve(implementations.size());
  394|  1.72k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.29k, False: 432]
  ------------------
  395|  1.29k|      auto impl = implementations[i];
  396|  1.29k|      const ToType canary1{42};
  397|  1.29k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.29k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.29k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.29k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.29k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.29k|          return r != 0;
  404|  1.29k|        } else {
  405|  1.29k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.29k|        }
  407|  1.29k|      }(implret1);
  408|  1.29k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.29k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  1.29k|        const ToType canary2{25};
  414|  1.29k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.29k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.29k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.29k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.29k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.29k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.29k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 312, False: 984]
  |  Branch (423:29): [True: 312, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    312|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    312|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 312]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    312|        }
  440|  1.29k|      }
  441|  1.29k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 312, False: 984]
  ------------------
  442|  1.29k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    432|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 328, False: 104]
  ------------------
  447|    984|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 984, False: 328]
  ------------------
  448|    984|        e.outputhash.clear();
  449|    984|      }
  450|    328|    }
  451|       |
  452|    432|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    432|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 432]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    432|    } else {
  474|    432|      ret.implementations_agree = true;
  475|    432|    }
  476|    432|    return ret;
  477|    432|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|  1.29k|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|  1.29k|        } else {
  405|  1.29k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.29k|        }
  407|  1.29k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    864|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_34clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    365|    +[](std::span<const char> chardata) {                                      \
  555|    365|      const auto c =                                                           \
  556|    365|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    365|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    365|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    365|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    365|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    365|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    365|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    365|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    365|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    365|  constexpr auto _size = _name.size();                                \
  |  | 1183|    365|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    365|  return _nameof; }()
  ------------------
  561|    365|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    365|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    365|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    365|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    365|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    365|  constexpr auto _size = _name.size();                                \
  |  | 1183|    365|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    365|  return _nameof; }()
  ------------------
  562|    365|      c.fuzz(chardata);                                                        \
  563|    365|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    365|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    365|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    365|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    365|    static const bool do_print_testcase =
  181|    365|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    365|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 365]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    365|    do {
  189|       |      // step 0 - is the input valid?
  190|    365|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    365|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 365]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    365|                    From == UtfEncodings::UTF8) {
  198|    365|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 365]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    365|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    365|      const auto [output_length, length_agree] =
  204|    365|          calculate_length(from, inputisvalid);
  205|    365|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 365]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    365|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 230, False: 135]
  |  Branch (208:28): [True: 0, False: 230]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    365|      const auto [written, outputs_agree] =
  215|    365|          do_conversion(from, output_length, inputisvalid);
  216|    365|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 365]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    365|      return;
  221|    365|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    365|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    365|  validation_result verify_valid_input(FromSpan src) const {
  231|    365|    validation_result ret{};
  232|       |
  233|    365|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    365|    const auto implementations = get_supported_implementations();
  235|    365|    std::vector<simdutf::result> results;
  236|    365|    results.reserve(implementations.size());
  237|       |
  238|  1.09k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.09k, False: 365]
  ------------------
  239|  1.09k|      results.push_back(
  240|  1.09k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.09k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.09k|      const bool validation2 =
  245|  1.09k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.09k|                      src.data(), src.size());
  247|  1.09k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.09k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  1.09k|    }
  258|       |
  259|    365|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    365|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 365]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    365|    } else {
  273|    365|      ret.implementations_agree = true;
  274|    365|    }
  275|    365|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    365|      return r.error == simdutf::SUCCESS;
  277|    365|    });
  278|    365|    return ret;
  279|    365|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    730|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKS7_E_clESI_:
  275|    635|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    635|      return r.error == simdutf::SUCCESS;
  277|    635|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    365|  bool count_the_input(FromSpan src) const {
  290|    365|    const auto implementations = get_supported_implementations();
  291|    365|    std::vector<std::size_t> results;
  292|    365|    results.reserve(implementations.size());
  293|       |
  294|  1.09k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.09k, False: 365]
  ------------------
  295|  1.09k|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|       |      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|       |        ret = impl->count_utf16le(src.data(), src.size());
  300|  1.09k|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|  1.09k|        ret = impl->count_utf8(src.data(), src.size());
  302|  1.09k|      }
  303|  1.09k|      results.push_back(ret);
  304|  1.09k|    }
  305|    365|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    365|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 365]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    365|    return true;
  321|    365|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    730|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    365|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    365|    length_result ret{};
  344|       |
  345|    365|    const auto implementations = get_supported_implementations();
  346|    365|    std::vector<std::size_t> results;
  347|    365|    results.reserve(implementations.size());
  348|       |
  349|  1.09k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.09k, False: 365]
  ------------------
  350|  1.09k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.09k|      results.push_back(len);
  352|  1.09k|      ret.length.push_back(len);
  353|  1.09k|    }
  354|       |
  355|    365|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    365|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 365]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    365|    } else {
  375|    365|      ret.implementations_agree = true;
  376|    365|    }
  377|    365|    return ret;
  378|    365|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.09k|                                FromSpan src) const {
  331|  1.09k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.09k|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    730|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    365|                                  const bool inputisvalid) const {
  383|    365|    conversion_result ret{};
  384|       |
  385|    365|    const auto implementations = get_supported_implementations();
  386|       |
  387|    365|    std::vector<result<ConversionResult>> results;
  388|    365|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    365|    std::vector<std::vector<ToType>> outputbuffers;
  393|    365|    outputbuffers.reserve(implementations.size());
  394|  1.46k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.09k, False: 365]
  ------------------
  395|  1.09k|      auto impl = implementations[i];
  396|  1.09k|      const ToType canary1{42};
  397|  1.09k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.09k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.09k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.09k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.09k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.09k|          return r != 0;
  404|  1.09k|        } else {
  405|  1.09k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.09k|        }
  407|  1.09k|      }(implret1);
  408|  1.09k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.09k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  1.09k|        const ToType canary2{25};
  414|  1.09k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.09k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.09k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.09k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.09k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.09k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.09k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 405, False: 690]
  |  Branch (423:29): [True: 378, False: 27]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    378|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    378|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 378]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    378|        }
  440|  1.09k|      }
  441|  1.09k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 378, False: 717]
  ------------------
  442|  1.09k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    365|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 230, False: 135]
  ------------------
  447|    690|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 690, False: 230]
  ------------------
  448|    690|        e.outputhash.clear();
  449|    690|      }
  450|    230|    }
  451|       |
  452|    365|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    365|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 365]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    365|    } else {
  474|    365|      ret.implementations_agree = true;
  475|    365|    }
  476|    365|    return ret;
  477|    365|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|  1.09k|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|  1.09k|        } else {
  405|  1.09k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.09k|        }
  407|  1.09k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    730|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_35clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    467|    +[](std::span<const char> chardata) {                                      \
  555|    467|      const auto c =                                                           \
  556|    467|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    467|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    467|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    467|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    467|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    467|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    467|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    467|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    467|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    467|  constexpr auto _size = _name.size();                                \
  |  | 1183|    467|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    467|  return _nameof; }()
  ------------------
  561|    467|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    467|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    467|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    467|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    467|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    467|  constexpr auto _size = _name.size();                                \
  |  | 1183|    467|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    467|  return _nameof; }()
  ------------------
  562|    467|      c.fuzz(chardata);                                                        \
  563|    467|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    467|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    467|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    467|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    467|    static const bool do_print_testcase =
  181|    467|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    467|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 467]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    467|    do {
  189|       |      // step 0 - is the input valid?
  190|    467|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    467|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 467]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    467|                    From == UtfEncodings::UTF8) {
  198|    467|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 467]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    467|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    467|      const auto [output_length, length_agree] =
  204|    467|          calculate_length(from, inputisvalid);
  205|    467|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 467]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    467|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 295, False: 172]
  |  Branch (208:28): [True: 0, False: 295]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    467|      const auto [written, outputs_agree] =
  215|    467|          do_conversion(from, output_length, inputisvalid);
  216|    467|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 467]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    467|      return;
  221|    467|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    467|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    467|  validation_result verify_valid_input(FromSpan src) const {
  231|    467|    validation_result ret{};
  232|       |
  233|    467|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    467|    const auto implementations = get_supported_implementations();
  235|    467|    std::vector<simdutf::result> results;
  236|    467|    results.reserve(implementations.size());
  237|       |
  238|  1.40k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.40k, False: 467]
  ------------------
  239|  1.40k|      results.push_back(
  240|  1.40k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.40k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.40k|      const bool validation2 =
  245|  1.40k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.40k|                      src.data(), src.size());
  247|  1.40k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.40k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  1.40k|    }
  258|       |
  259|    467|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    467|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 467]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    467|    } else {
  273|    467|      ret.implementations_agree = true;
  274|    467|    }
  275|    467|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    467|      return r.error == simdutf::SUCCESS;
  277|    467|    });
  278|    467|    return ret;
  279|    467|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    934|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKS7_E_clESI_:
  275|    811|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    811|      return r.error == simdutf::SUCCESS;
  277|    811|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    467|  bool count_the_input(FromSpan src) const {
  290|    467|    const auto implementations = get_supported_implementations();
  291|    467|    std::vector<std::size_t> results;
  292|    467|    results.reserve(implementations.size());
  293|       |
  294|  1.40k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.40k, False: 467]
  ------------------
  295|  1.40k|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|       |      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|       |        ret = impl->count_utf16le(src.data(), src.size());
  300|  1.40k|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|  1.40k|        ret = impl->count_utf8(src.data(), src.size());
  302|  1.40k|      }
  303|  1.40k|      results.push_back(ret);
  304|  1.40k|    }
  305|    467|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    467|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 467]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    467|    return true;
  321|    467|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    934|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    467|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    467|    length_result ret{};
  344|       |
  345|    467|    const auto implementations = get_supported_implementations();
  346|    467|    std::vector<std::size_t> results;
  347|    467|    results.reserve(implementations.size());
  348|       |
  349|  1.40k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.40k, False: 467]
  ------------------
  350|  1.40k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.40k|      results.push_back(len);
  352|  1.40k|      ret.length.push_back(len);
  353|  1.40k|    }
  354|       |
  355|    467|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    467|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 467]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    467|    } else {
  375|    467|      ret.implementations_agree = true;
  376|    467|    }
  377|    467|    return ret;
  378|    467|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.40k|                                FromSpan src) const {
  331|  1.40k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.40k|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    934|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    467|                                  const bool inputisvalid) const {
  383|    467|    conversion_result ret{};
  384|       |
  385|    467|    const auto implementations = get_supported_implementations();
  386|       |
  387|    467|    std::vector<result<ConversionResult>> results;
  388|    467|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    467|    std::vector<std::vector<ToType>> outputbuffers;
  393|    467|    outputbuffers.reserve(implementations.size());
  394|  1.86k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.40k, False: 467]
  ------------------
  395|  1.40k|      auto impl = implementations[i];
  396|  1.40k|      const ToType canary1{42};
  397|  1.40k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.40k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.40k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.40k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.40k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.40k|          return r != 0;
  404|  1.40k|        } else {
  405|  1.40k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.40k|        }
  407|  1.40k|      }(implret1);
  408|  1.40k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.40k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  1.40k|        const ToType canary2{25};
  414|  1.40k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.40k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.40k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.40k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.40k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.40k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.40k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 516, False: 885]
  |  Branch (423:29): [True: 516, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    516|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    516|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 516]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    516|        }
  440|  1.40k|      }
  441|  1.40k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 516, False: 885]
  ------------------
  442|  1.40k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    467|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 295, False: 172]
  ------------------
  447|    885|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 885, False: 295]
  ------------------
  448|    885|        e.outputhash.clear();
  449|    885|      }
  450|    295|    }
  451|       |
  452|    467|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    467|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 467]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    467|    } else {
  474|    467|      ret.implementations_agree = true;
  475|    467|    }
  476|    467|    return ret;
  477|    467|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|  1.40k|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|  1.40k|        } else {
  405|  1.40k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.40k|        }
  407|  1.40k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    934|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_36clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    373|    +[](std::span<const char> chardata) {                                      \
  555|    373|      const auto c =                                                           \
  556|    373|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    373|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    373|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    373|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    373|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    373|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    373|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    373|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    373|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    373|  constexpr auto _size = _name.size();                                \
  |  | 1183|    373|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    373|  return _nameof; }()
  ------------------
  561|    373|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    373|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    373|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    373|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    373|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    373|  constexpr auto _size = _name.size();                                \
  |  | 1183|    373|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    373|  return _nameof; }()
  ------------------
  562|    373|      c.fuzz(chardata);                                                        \
  563|    373|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    373|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    373|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    373|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    373|    static const bool do_print_testcase =
  181|    373|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    373|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 373]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    373|    do {
  189|       |      // step 0 - is the input valid?
  190|    373|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    373|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 373]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    373|                    From == UtfEncodings::UTF8) {
  198|    373|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 373]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    373|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    373|      const auto [output_length, length_agree] =
  204|    373|          calculate_length(from, inputisvalid);
  205|    373|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 373]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    373|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 253, False: 120]
  |  Branch (208:28): [True: 0, False: 253]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    373|      const auto [written, outputs_agree] =
  215|    373|          do_conversion(from, output_length, inputisvalid);
  216|    373|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 373]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    373|      return;
  221|    373|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    373|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    373|  validation_result verify_valid_input(FromSpan src) const {
  231|    373|    validation_result ret{};
  232|       |
  233|    373|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    373|    const auto implementations = get_supported_implementations();
  235|    373|    std::vector<simdutf::result> results;
  236|    373|    results.reserve(implementations.size());
  237|       |
  238|  1.11k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.11k, False: 373]
  ------------------
  239|  1.11k|      results.push_back(
  240|  1.11k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.11k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.11k|      const bool validation2 =
  245|  1.11k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.11k|                      src.data(), src.size());
  247|  1.11k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.11k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  1.11k|    }
  258|       |
  259|    373|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    373|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 373]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    373|    } else {
  273|    373|      ret.implementations_agree = true;
  274|    373|    }
  275|    373|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    373|      return r.error == simdutf::SUCCESS;
  277|    373|    });
  278|    373|    return ret;
  279|    373|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    746|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKS7_E_clESI_:
  275|    613|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    613|      return r.error == simdutf::SUCCESS;
  277|    613|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    373|  bool count_the_input(FromSpan src) const {
  290|    373|    const auto implementations = get_supported_implementations();
  291|    373|    std::vector<std::size_t> results;
  292|    373|    results.reserve(implementations.size());
  293|       |
  294|  1.11k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.11k, False: 373]
  ------------------
  295|  1.11k|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|       |      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|       |        ret = impl->count_utf16le(src.data(), src.size());
  300|  1.11k|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|  1.11k|        ret = impl->count_utf8(src.data(), src.size());
  302|  1.11k|      }
  303|  1.11k|      results.push_back(ret);
  304|  1.11k|    }
  305|    373|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    373|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 373]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    373|    return true;
  321|    373|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    746|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    373|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    373|    length_result ret{};
  344|       |
  345|    373|    const auto implementations = get_supported_implementations();
  346|    373|    std::vector<std::size_t> results;
  347|    373|    results.reserve(implementations.size());
  348|       |
  349|  1.11k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.11k, False: 373]
  ------------------
  350|  1.11k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.11k|      results.push_back(len);
  352|  1.11k|      ret.length.push_back(len);
  353|  1.11k|    }
  354|       |
  355|    373|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    373|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 373]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    373|    } else {
  375|    373|      ret.implementations_agree = true;
  376|    373|    }
  377|    373|    return ret;
  378|    373|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.11k|                                FromSpan src) const {
  331|  1.11k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.11k|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    746|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    373|                                  const bool inputisvalid) const {
  383|    373|    conversion_result ret{};
  384|       |
  385|    373|    const auto implementations = get_supported_implementations();
  386|       |
  387|    373|    std::vector<result<ConversionResult>> results;
  388|    373|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    373|    std::vector<std::vector<ToType>> outputbuffers;
  393|    373|    outputbuffers.reserve(implementations.size());
  394|  1.49k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.11k, False: 373]
  ------------------
  395|  1.11k|      auto impl = implementations[i];
  396|  1.11k|      const ToType canary1{42};
  397|  1.11k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.11k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.11k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.11k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.11k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.11k|          return r != 0;
  404|  1.11k|        } else {
  405|  1.11k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.11k|        }
  407|  1.11k|      }(implret1);
  408|  1.11k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.11k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  1.11k|        const ToType canary2{25};
  414|  1.11k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.11k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.11k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.11k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.11k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.11k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.11k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 360, False: 759]
  |  Branch (423:29): [True: 360, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    360|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    360|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 360]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    360|        }
  440|  1.11k|      }
  441|  1.11k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 360, False: 759]
  ------------------
  442|  1.11k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    373|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 253, False: 120]
  ------------------
  447|    759|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 759, False: 253]
  ------------------
  448|    759|        e.outputhash.clear();
  449|    759|      }
  450|    253|    }
  451|       |
  452|    373|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    373|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 373]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    373|    } else {
  474|    373|      ret.implementations_agree = true;
  475|    373|    }
  476|    373|    return ret;
  477|    373|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|  1.11k|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|  1.11k|        } else {
  405|  1.11k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.11k|        }
  407|  1.11k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    746|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_37clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    463|    +[](std::span<const char> chardata) {                                      \
  555|    463|      const auto c =                                                           \
  556|    463|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    463|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    463|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    463|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    463|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    463|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    463|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    463|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    463|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    463|  constexpr auto _size = _name.size();                                \
  |  | 1183|    463|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    463|  return _nameof; }()
  ------------------
  561|    463|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    463|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    463|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    463|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    463|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    463|  constexpr auto _size = _name.size();                                \
  |  | 1183|    463|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    463|  return _nameof; }()
  ------------------
  562|    463|      c.fuzz(chardata);                                                        \
  563|    463|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    463|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    463|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    463|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    463|    static const bool do_print_testcase =
  181|    463|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    463|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 463]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    463|    do {
  189|       |      // step 0 - is the input valid?
  190|    463|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    463|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 463]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|    463|                    From == UtfEncodings::UTF8) {
  198|    463|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 463]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    463|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    463|      const auto [output_length, length_agree] =
  204|    463|          calculate_length(from, inputisvalid);
  205|    463|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 463]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    463|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 282, False: 181]
  |  Branch (208:28): [True: 0, False: 282]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    463|      const auto [written, outputs_agree] =
  215|    463|          do_conversion(from, output_length, inputisvalid);
  216|    463|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 463]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    463|      return;
  221|    463|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    463|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    463|  validation_result verify_valid_input(FromSpan src) const {
  231|    463|    validation_result ret{};
  232|       |
  233|    463|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    463|    const auto implementations = get_supported_implementations();
  235|    463|    std::vector<simdutf::result> results;
  236|    463|    results.reserve(implementations.size());
  237|       |
  238|  1.38k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.38k, False: 463]
  ------------------
  239|  1.38k|      results.push_back(
  240|  1.38k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.38k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.38k|      const bool validation2 =
  245|  1.38k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.38k|                      src.data(), src.size());
  247|  1.38k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.38k]
  ------------------
  248|      0|        std::cerr << "begin errormessage for verify_valid_input()\n";
  249|      0|        std::cerr << ValidationFunctionTrait<From>::ValidationWithErrorsName
  250|      0|                  << " gives " << validation1 << " while "
  251|      0|                  << ValidationFunctionTrait<From>::ValidationName << " gave "
  252|      0|                  << validation2 << " for implementation " << impl->name()
  253|      0|                  << '\n';
  254|      0|        std::cerr << "end errormessage\n";
  255|      0|        std::abort();
  256|      0|      }
  257|  1.38k|    }
  258|       |
  259|    463|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    463|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 463]
  ------------------
  261|      0|      std::cerr << "begin errormessage for verify_valid_input()\n";
  262|      0|      std::cerr << "in fuzz case for "
  263|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  264|      0|                << " invoked with " << src.size() << " elements:\n";
  265|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (265:31): [True: 0, False: 0]
  ------------------
  266|      0|        std::cerr << "got return " << std::dec << results[i]
  267|      0|                  << " from implementation " << implementations[i]->name()
  268|      0|                  << '\n';
  269|      0|      }
  270|      0|      std::cerr << "end errormessage\n";
  271|      0|      ret.implementations_agree = false;
  272|    463|    } else {
  273|    463|      ret.implementations_agree = true;
  274|    463|    }
  275|    463|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    463|      return r.error == simdutf::SUCCESS;
  277|    463|    });
  278|    463|    return ret;
  279|    463|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    926|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKS7_E_clESI_:
  275|    825|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    825|      return r.error == simdutf::SUCCESS;
  277|    825|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    463|  bool count_the_input(FromSpan src) const {
  290|    463|    const auto implementations = get_supported_implementations();
  291|    463|    std::vector<std::size_t> results;
  292|    463|    results.reserve(implementations.size());
  293|       |
  294|  1.38k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.38k, False: 463]
  ------------------
  295|  1.38k|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|       |      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|       |        ret = impl->count_utf16le(src.data(), src.size());
  300|  1.38k|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|  1.38k|        ret = impl->count_utf8(src.data(), src.size());
  302|  1.38k|      }
  303|  1.38k|      results.push_back(ret);
  304|  1.38k|    }
  305|    463|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    463|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 463]
  ------------------
  307|      0|      std::cerr << "begin errormessage for count_the_input()\n";
  308|      0|      std::cerr << "in fuzz case for "
  309|      0|                << ValidationFunctionTrait<From>::ValidationWithErrorsName
  310|      0|                << " invoked with " << src.size() << " elements:\n";
  311|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (311:31): [True: 0, False: 0]
  ------------------
  312|      0|        std::cerr << "got return " << std::dec << results[i]
  313|      0|                  << " from implementation " << implementations[i]->name()
  314|      0|                  << '\n';
  315|      0|      }
  316|      0|      std::cerr << "end errormessage\n";
  317|      0|      return false;
  318|      0|    }
  319|       |
  320|    463|    return true;
  321|    463|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    926|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    463|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    463|    length_result ret{};
  344|       |
  345|    463|    const auto implementations = get_supported_implementations();
  346|    463|    std::vector<std::size_t> results;
  347|    463|    results.reserve(implementations.size());
  348|       |
  349|  1.38k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.38k, False: 463]
  ------------------
  350|  1.38k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.38k|      results.push_back(len);
  352|  1.38k|      ret.length.push_back(len);
  353|  1.38k|    }
  354|       |
  355|    463|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    463|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 463]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    463|    } else {
  375|    463|      ret.implementations_agree = true;
  376|    463|    }
  377|    463|    return ret;
  378|    463|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.38k|                                FromSpan src) const {
  331|  1.38k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.38k|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    926|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    463|                                  const bool inputisvalid) const {
  383|    463|    conversion_result ret{};
  384|       |
  385|    463|    const auto implementations = get_supported_implementations();
  386|       |
  387|    463|    std::vector<result<ConversionResult>> results;
  388|    463|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    463|    std::vector<std::vector<ToType>> outputbuffers;
  393|    463|    outputbuffers.reserve(implementations.size());
  394|  1.85k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.38k, False: 463]
  ------------------
  395|  1.38k|      auto impl = implementations[i];
  396|  1.38k|      const ToType canary1{42};
  397|  1.38k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.38k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.38k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.38k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.38k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.38k|          return r != 0;
  404|  1.38k|        } else {
  405|  1.38k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.38k|        }
  407|  1.38k|      }(implret1);
  408|  1.38k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.38k|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|  1.38k|        const ToType canary2{25};
  414|  1.38k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.38k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.38k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.38k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.38k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.38k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.38k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 543, False: 846]
  |  Branch (423:29): [True: 543, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    543|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    543|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 543]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    543|        }
  440|  1.38k|      }
  441|  1.38k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 543, False: 846]
  ------------------
  442|  1.38k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    463|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 282, False: 181]
  ------------------
  447|    846|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 846, False: 282]
  ------------------
  448|    846|        e.outputhash.clear();
  449|    846|      }
  450|    282|    }
  451|       |
  452|    463|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    463|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 463]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    463|    } else {
  474|    463|      ret.implementations_agree = true;
  475|    463|    }
  476|    463|    return ret;
  477|    463|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|  1.38k|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|  1.38k|        } else {
  405|  1.38k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.38k|        }
  407|  1.38k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    926|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_38clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     48|    +[](std::span<const char> chardata) {                                      \
  555|     48|      const auto c =                                                           \
  556|     48|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     48|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     48|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     48|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     48|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     48|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     48|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     48|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     48|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     48|  constexpr auto _size = _name.size();                                \
  |  | 1183|     48|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     48|  return _nameof; }()
  ------------------
  561|     48|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     48|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     48|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     48|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     48|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     48|  constexpr auto _size = _name.size();                                \
  |  | 1183|     48|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     48|  return _nameof; }()
  ------------------
  562|     48|      c.fuzz(chardata);                                                        \
  563|     48|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE4fuzzENSt3__14spanIS5_Lm18446744073709551615EEE:
  175|     48|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|     48|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|     48|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|     48|    static const bool do_print_testcase =
  181|     48|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|     48|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 48]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|     48|    do {
  189|       |      // step 0 - is the input valid?
  190|     48|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|     48|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 48]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|       |                    From == UtfEncodings::UTF8) {
  198|       |        if (!count_the_input(from) && !allow_implementations_to_differ)
  199|       |          break;
  200|       |      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|     48|      const auto [output_length, length_agree] =
  204|     48|          calculate_length(from, inputisvalid);
  205|     48|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 48]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|     48|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 0, False: 48]
  |  Branch (208:28): [True: 0, False: 0]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|     48|      const auto [written, outputs_agree] =
  215|     48|          do_conversion(from, output_length, inputisvalid);
  216|     48|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 48]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|     48|      return;
  221|     48|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|     48|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE18verify_valid_inputIvQeqT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEE:
  283|     48|  validation_result verify_valid_input(FromSpan) const {
  284|       |    // all latin1 input is valid. there is no simdutf validation function for
  285|       |    // it.
  286|     48|    return validation_result{.valid = true, .implementations_agree = true};
  287|     48|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEb:
  342|     48|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|     48|    length_result ret{};
  344|       |
  345|     48|    const auto implementations = get_supported_implementations();
  346|     48|    std::vector<std::size_t> results;
  347|     48|    results.reserve(implementations.size());
  348|       |
  349|    144|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 144, False: 48]
  ------------------
  350|    144|      const auto len = invoke_lengthcalc(impl, src);
  351|    144|      results.push_back(len);
  352|    144|      ret.length.push_back(len);
  353|    144|    }
  354|       |
  355|     48|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|     48|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 48]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|     48|    } else {
  375|     48|      ret.implementations_agree = true;
  376|     48|    }
  377|     48|    return ret;
  378|     48|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSE_NSt3__14spanIS5_Lm18446744073709551615EEE:
  338|    144|                                FromSpan src) const {
  339|    144|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    144|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|     96|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|     48|                                  const bool inputisvalid) const {
  383|     48|    conversion_result ret{};
  384|       |
  385|     48|    const auto implementations = get_supported_implementations();
  386|       |
  387|     48|    std::vector<result<ConversionResult>> results;
  388|     48|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|     48|    std::vector<std::vector<ToType>> outputbuffers;
  393|     48|    outputbuffers.reserve(implementations.size());
  394|    192|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 144, False: 48]
  ------------------
  395|    144|      auto impl = implementations[i];
  396|    144|      const ToType canary1{42};
  397|    144|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    144|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    144|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    144|      const auto success = [](const ConversionResult& r) -> bool {
  402|    144|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    144|          return r != 0;
  404|    144|        } else {
  405|    144|          return r.error == simdutf::error_code::SUCCESS;
  406|    144|        }
  407|    144|      }(implret1);
  408|    144|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    144|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    144|        const ToType canary2{25};
  414|    144|        const auto outputbuffer_first_run = outputbuffer;
  415|    144|        std::ranges::fill(outputbuffer, canary2);
  416|    144|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    144|                                          src.size(), outputbuffer.data());
  418|       |
  419|    144|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 144]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    144|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 144, False: 0]
  |  Branch (423:29): [True: 138, False: 6]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    138|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    138|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 138]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    138|        }
  440|    144|      }
  441|    144|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 138, False: 6]
  ------------------
  442|    144|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|     48|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 0, False: 48]
  ------------------
  447|      0|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 0, False: 0]
  ------------------
  448|      0|        e.outputhash.clear();
  449|      0|      }
  450|      0|    }
  451|       |
  452|     48|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|     48|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 48]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|     48|    } else {
  474|     48|      ret.implementations_agree = true;
  475|     48|    }
  476|     48|    return ret;
  477|     48|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    144|      const auto success = [](const ConversionResult& r) -> bool {
  402|    144|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    144|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    144|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|     96|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_39clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     55|    +[](std::span<const char> chardata) {                                      \
  555|     55|      const auto c =                                                           \
  556|     55|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     55|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     55|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     55|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     55|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     55|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     55|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     55|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     55|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     55|  constexpr auto _size = _name.size();                                \
  |  | 1183|     55|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     55|  return _nameof; }()
  ------------------
  561|     55|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     55|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     55|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     55|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     55|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     55|  constexpr auto _size = _name.size();                                \
  |  | 1183|     55|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     55|  return _nameof; }()
  ------------------
  562|     55|      c.fuzz(chardata);                                                        \
  563|     55|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE4fuzzENSt3__14spanIS5_Lm18446744073709551615EEE:
  175|     55|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|     55|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|     55|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|     55|    static const bool do_print_testcase =
  181|     55|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|     55|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 55]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|     55|    do {
  189|       |      // step 0 - is the input valid?
  190|     55|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|     55|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 55]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|       |                    From == UtfEncodings::UTF8) {
  198|       |        if (!count_the_input(from) && !allow_implementations_to_differ)
  199|       |          break;
  200|       |      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|     55|      const auto [output_length, length_agree] =
  204|     55|          calculate_length(from, inputisvalid);
  205|     55|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 55]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|     55|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 0, False: 55]
  |  Branch (208:28): [True: 0, False: 0]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|     55|      const auto [written, outputs_agree] =
  215|     55|          do_conversion(from, output_length, inputisvalid);
  216|     55|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 55]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|     55|      return;
  221|     55|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|     55|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE18verify_valid_inputIvQeqT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEE:
  283|     55|  validation_result verify_valid_input(FromSpan) const {
  284|       |    // all latin1 input is valid. there is no simdutf validation function for
  285|       |    // it.
  286|     55|    return validation_result{.valid = true, .implementations_agree = true};
  287|     55|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEb:
  342|     55|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|     55|    length_result ret{};
  344|       |
  345|     55|    const auto implementations = get_supported_implementations();
  346|     55|    std::vector<std::size_t> results;
  347|     55|    results.reserve(implementations.size());
  348|       |
  349|    165|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 165, False: 55]
  ------------------
  350|    165|      const auto len = invoke_lengthcalc(impl, src);
  351|    165|      results.push_back(len);
  352|    165|      ret.length.push_back(len);
  353|    165|    }
  354|       |
  355|     55|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|     55|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 55]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|     55|    } else {
  375|     55|      ret.implementations_agree = true;
  376|     55|    }
  377|     55|    return ret;
  378|     55|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSE_NSt3__14spanIS5_Lm18446744073709551615EEE:
  338|    165|                                FromSpan src) const {
  339|    165|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    165|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    110|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|     55|                                  const bool inputisvalid) const {
  383|     55|    conversion_result ret{};
  384|       |
  385|     55|    const auto implementations = get_supported_implementations();
  386|       |
  387|     55|    std::vector<result<ConversionResult>> results;
  388|     55|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|     55|    std::vector<std::vector<ToType>> outputbuffers;
  393|     55|    outputbuffers.reserve(implementations.size());
  394|    220|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 165, False: 55]
  ------------------
  395|    165|      auto impl = implementations[i];
  396|    165|      const ToType canary1{42};
  397|    165|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    165|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    165|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    165|      const auto success = [](const ConversionResult& r) -> bool {
  402|    165|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    165|          return r != 0;
  404|    165|        } else {
  405|    165|          return r.error == simdutf::error_code::SUCCESS;
  406|    165|        }
  407|    165|      }(implret1);
  408|    165|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    165|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    165|        const ToType canary2{25};
  414|    165|        const auto outputbuffer_first_run = outputbuffer;
  415|    165|        std::ranges::fill(outputbuffer, canary2);
  416|    165|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    165|                                          src.size(), outputbuffer.data());
  418|       |
  419|    165|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 165]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    165|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 165, False: 0]
  |  Branch (423:29): [True: 159, False: 6]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    159|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    159|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 159]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    159|        }
  440|    165|      }
  441|    165|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 159, False: 6]
  ------------------
  442|    165|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|     55|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 0, False: 55]
  ------------------
  447|      0|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 0, False: 0]
  ------------------
  448|      0|        e.outputhash.clear();
  449|      0|      }
  450|      0|    }
  451|       |
  452|     55|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|     55|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 55]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|     55|    } else {
  474|     55|      ret.implementations_agree = true;
  475|     55|    }
  476|     55|    return ret;
  477|     55|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    165|      const auto success = [](const ConversionResult& r) -> bool {
  402|    165|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    165|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    165|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    110|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_40clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     38|    +[](std::span<const char> chardata) {                                      \
  555|     38|      const auto c =                                                           \
  556|     38|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     38|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     38|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     38|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     38|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     38|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     38|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     38|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     38|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     38|  constexpr auto _size = _name.size();                                \
  |  | 1183|     38|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     38|  return _nameof; }()
  ------------------
  561|     38|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     38|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     38|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     38|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     38|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     38|  constexpr auto _size = _name.size();                                \
  |  | 1183|     38|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     38|  return _nameof; }()
  ------------------
  562|     38|      c.fuzz(chardata);                                                        \
  563|     38|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE4fuzzENSt3__14spanIS5_Lm18446744073709551615EEE:
  175|     38|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|     38|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|     38|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|     38|    static const bool do_print_testcase =
  181|     38|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|     38|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 38]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|     38|    do {
  189|       |      // step 0 - is the input valid?
  190|     38|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|     38|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 38]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|       |                    From == UtfEncodings::UTF8) {
  198|       |        if (!count_the_input(from) && !allow_implementations_to_differ)
  199|       |          break;
  200|       |      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|     38|      const auto [output_length, length_agree] =
  204|     38|          calculate_length(from, inputisvalid);
  205|     38|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 38]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|     38|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 0, False: 38]
  |  Branch (208:28): [True: 0, False: 0]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|     38|      const auto [written, outputs_agree] =
  215|     38|          do_conversion(from, output_length, inputisvalid);
  216|     38|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 38]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|     38|      return;
  221|     38|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|     38|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE18verify_valid_inputIvQeqT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEE:
  283|     38|  validation_result verify_valid_input(FromSpan) const {
  284|       |    // all latin1 input is valid. there is no simdutf validation function for
  285|       |    // it.
  286|     38|    return validation_result{.valid = true, .implementations_agree = true};
  287|     38|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEb:
  342|     38|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|     38|    length_result ret{};
  344|       |
  345|     38|    const auto implementations = get_supported_implementations();
  346|     38|    std::vector<std::size_t> results;
  347|     38|    results.reserve(implementations.size());
  348|       |
  349|    114|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 114, False: 38]
  ------------------
  350|    114|      const auto len = invoke_lengthcalc(impl, src);
  351|    114|      results.push_back(len);
  352|    114|      ret.length.push_back(len);
  353|    114|    }
  354|       |
  355|     38|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|     38|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 38]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|     38|    } else {
  375|     38|      ret.implementations_agree = true;
  376|     38|    }
  377|     38|    return ret;
  378|     38|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSE_NSt3__14spanIS5_Lm18446744073709551615EEE:
  338|    114|                                FromSpan src) const {
  339|    114|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    114|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|     76|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|     38|                                  const bool inputisvalid) const {
  383|     38|    conversion_result ret{};
  384|       |
  385|     38|    const auto implementations = get_supported_implementations();
  386|       |
  387|     38|    std::vector<result<ConversionResult>> results;
  388|     38|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|     38|    std::vector<std::vector<ToType>> outputbuffers;
  393|     38|    outputbuffers.reserve(implementations.size());
  394|    152|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 114, False: 38]
  ------------------
  395|    114|      auto impl = implementations[i];
  396|    114|      const ToType canary1{42};
  397|    114|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    114|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    114|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    114|      const auto success = [](const ConversionResult& r) -> bool {
  402|    114|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    114|          return r != 0;
  404|    114|        } else {
  405|    114|          return r.error == simdutf::error_code::SUCCESS;
  406|    114|        }
  407|    114|      }(implret1);
  408|    114|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    114|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    114|        const ToType canary2{25};
  414|    114|        const auto outputbuffer_first_run = outputbuffer;
  415|    114|        std::ranges::fill(outputbuffer, canary2);
  416|    114|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    114|                                          src.size(), outputbuffer.data());
  418|       |
  419|    114|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 114]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    114|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 114, False: 0]
  |  Branch (423:29): [True: 108, False: 6]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    108|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    108|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 108]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    108|        }
  440|    114|      }
  441|    114|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 108, False: 6]
  ------------------
  442|    114|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|     38|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 0, False: 38]
  ------------------
  447|      0|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 0, False: 0]
  ------------------
  448|      0|        e.outputhash.clear();
  449|      0|      }
  450|      0|    }
  451|       |
  452|     38|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|     38|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 38]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|     38|    } else {
  474|     38|      ret.implementations_agree = true;
  475|     38|    }
  476|     38|    return ret;
  477|     38|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    114|      const auto success = [](const ConversionResult& r) -> bool {
  402|    114|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    114|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    114|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|     76|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_41clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    280|    +[](std::span<const char> chardata) {                                      \
  555|    280|      const auto c =                                                           \
  556|    280|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    280|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    280|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    280|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    280|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    280|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    280|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    280|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    280|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    280|  constexpr auto _size = _name.size();                                \
  |  | 1183|    280|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    280|  return _nameof; }()
  ------------------
  561|    280|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    280|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    280|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    280|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    280|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    280|  constexpr auto _size = _name.size();                                \
  |  | 1183|    280|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    280|  return _nameof; }()
  ------------------
  562|    280|      c.fuzz(chardata);                                                        \
  563|    280|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    280|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    280|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    280|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    280|    static const bool do_print_testcase =
  181|    280|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    280|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 280]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    280|    do {
  189|       |      // step 0 - is the input valid?
  190|    280|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    280|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 280]
  |  Branch (191:33): [True: 0, Folded]
  ------------------
  192|      0|        break;
  193|       |
  194|       |      // step 1 - count the input (only makes sense for some of the encodings)
  195|       |      if constexpr (From == UtfEncodings::UTF16BE ||
  196|       |                    From == UtfEncodings::UTF16LE ||
  197|       |                    From == UtfEncodings::UTF8) {
  198|       |        if (!count_the_input(from) && !allow_implementations_to_differ)
  199|       |          break;
  200|       |      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    280|      const auto [output_length, length_agree] =
  204|    280|          calculate_length(from, inputisvalid);
  205|    280|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 280]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    280|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 0, False: 280]
  |  Branch (208:28): [True: 0, False: 0]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    280|      const auto [written, outputs_agree] =
  215|    280|          do_conversion(from, output_length, inputisvalid);
  216|    280|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 280]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    280|      return;
  221|    280|    } while (0);
  ------------------
  |  Branch (221:14): [Folded, False: 0]
  ------------------
  222|       |    // if we come here, something failed
  223|      0|    std::cerr << "something failed, rerun with PRINT_FUZZ_CASE set to print a "
  224|      0|                 "reproducer to stderr\n";
  225|      0|    std::abort();
  226|    280|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQeqT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  283|    280|  validation_result verify_valid_input(FromSpan) const {
  284|       |    // all latin1 input is valid. there is no simdutf validation function for
  285|       |    // it.
  286|    280|    return validation_result{.valid = true, .implementations_agree = true};
  287|    280|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    280|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    280|    length_result ret{};
  344|       |
  345|    280|    const auto implementations = get_supported_implementations();
  346|    280|    std::vector<std::size_t> results;
  347|    280|    results.reserve(implementations.size());
  348|       |
  349|    840|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 840, False: 280]
  ------------------
  350|    840|      const auto len = invoke_lengthcalc(impl, src);
  351|    840|      results.push_back(len);
  352|    840|      ret.length.push_back(len);
  353|    840|    }
  354|       |
  355|    280|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    280|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 280]
  ------------------
  357|      0|      std::cerr << "begin errormessage for calculate_length\n";
  358|      0|      std::cerr << "in fuzz case invoking " << lengthcalcname << " with "
  359|      0|                << src.size() << " elements with valid input=" << inputisvalid
  360|      0|                << ":\n";
  361|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (361:31): [True: 0, False: 0]
  ------------------
  362|      0|        std::cerr << "got return " << std::dec << results[i]
  363|      0|                  << " from implementation " << implementations[i]->name()
  364|      0|                  << '\n';
  365|      0|      }
  366|      0|      std::cerr << "end errormessage\n";
  367|      0|      if (inputisvalid) {
  ------------------
  |  Branch (367:11): [True: 0, False: 0]
  ------------------
  368|      0|        ret.implementations_agree = false;
  369|      0|      } else {
  370|      0|        std::cerr
  371|      0|            << "implementations are allowed to disagree on invalid input\n";
  372|      0|        ret.implementations_agree = true;
  373|      0|      }
  374|    280|    } else {
  375|    280|      ret.implementations_agree = true;
  376|    280|    }
  377|    280|    return ret;
  378|    280|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    840|                                FromSpan src) const {
  331|    840|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    840|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    560|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    280|                                  const bool inputisvalid) const {
  383|    280|    conversion_result ret{};
  384|       |
  385|    280|    const auto implementations = get_supported_implementations();
  386|       |
  387|    280|    std::vector<result<ConversionResult>> results;
  388|    280|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    280|    std::vector<std::vector<ToType>> outputbuffers;
  393|    280|    outputbuffers.reserve(implementations.size());
  394|  1.12k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 840, False: 280]
  ------------------
  395|    840|      auto impl = implementations[i];
  396|    840|      const ToType canary1{42};
  397|    840|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    840|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    840|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    840|      const auto success = [](const ConversionResult& r) -> bool {
  402|    840|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    840|          return r != 0;
  404|    840|        } else {
  405|    840|          return r.error == simdutf::error_code::SUCCESS;
  406|    840|        }
  407|    840|      }(implret1);
  408|    840|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    840|      if constexpr (use_canary_in_output) {
  410|       |        // optionally convert again, this time with the buffer filled with
  411|       |        // a different value. if the output differs, it means some of the buffer
  412|       |        // was not written to by the conversion function.
  413|    840|        const ToType canary2{25};
  414|    840|        const auto outputbuffer_first_run = outputbuffer;
  415|    840|        std::ranges::fill(outputbuffer, canary2);
  416|    840|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    840|                                          src.size(), outputbuffer.data());
  418|       |
  419|    840|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 840]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    840|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 840, False: 0]
  |  Branch (423:29): [True: 834, False: 6]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    834|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    834|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 834]
  ------------------
  427|      0|            std::cerr << "different output the second time!\n";
  428|      0|            std::cerr << "implementation " << impl->name() << " " << name
  429|      0|                      << '\n';
  430|      0|            std::cerr << "input is valid=" << inputisvalid << '\n';
  431|      0|            std::cerr << "output length=" << outputbuffer.size() << '\n';
  432|      0|            std::cerr << "conversion was a success? " << success << '\n';
  433|      0|            for (std::size_t j = 0; j < outputbuffer.size(); ++j) {
  ------------------
  |  Branch (433:37): [True: 0, False: 0]
  ------------------
  434|      0|              std::cerr << "output[" << j << "]\t" << +outputbuffer_first_run[j]
  435|      0|                        << '\t' << +outputbuffer[j] << '\n';
  436|      0|            }
  437|      0|            std::abort();
  438|      0|          }
  439|    834|        }
  440|    840|      }
  441|    840|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 834, False: 6]
  ------------------
  442|    840|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    280|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 0, False: 280]
  ------------------
  447|      0|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 0, False: 0]
  ------------------
  448|      0|        e.outputhash.clear();
  449|      0|      }
  450|      0|    }
  451|       |
  452|    280|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    280|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 280]
  ------------------
  454|      0|      std::cerr << "begin errormessage for do_conversion\n";
  455|      0|      std::cerr << "in fuzz case for " << name << " invoked with " << src.size()
  456|      0|                << " elements:\n";
  457|      0|      std::cerr << "input data is valid ? " << inputisvalid << '\n';
  458|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (458:31): [True: 0, False: 0]
  ------------------
  459|      0|        std::cerr << "got return " << std::dec << results[i]
  460|      0|                  << " from implementation " << implementations[i]->name()
  461|      0|                  << " using outlen=" << outlength.at(i) << '\n';
  462|      0|      }
  463|      0|      for (std::size_t i = 0; i < results.size(); ++i) {
  ------------------
  |  Branch (463:31): [True: 0, False: 0]
  ------------------
  464|      0|        std::cerr << "implementation " << implementations[i]->name()
  465|      0|                  << " out: ";
  466|      0|        for (const auto e : outputbuffers.at(i)) {
  ------------------
  |  Branch (466:27): [True: 0, False: 0]
  ------------------
  467|      0|          std::cerr << +e << ", ";
  468|      0|        }
  469|      0|        std::cerr << '\n';
  470|      0|      }
  471|      0|      std::cerr << "end errormessage\n";
  472|      0|      ret.implementations_agree = false;
  473|    280|    } else {
  474|    280|      ret.implementations_agree = true;
  475|    280|    }
  476|    280|    return ret;
  477|    280|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    840|      const auto success = [](const ConversionResult& r) -> bool {
  402|    840|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    840|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    840|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    560|    auto neq = [](const auto& a, const auto& b) { return a != b; };

_ZN10FNV1A_hash14fnv1ahash_implENSt3__14spanIKcLm18446744073709551615EEE:
   75|  18.2k|  static constexpr std::uint64_t fnv1ahash_impl(std::span<const char> bytes) {
   76|  18.2k|    auto hash = offset;
   77|       |
   78|   642M|    for (auto byte : bytes) {
  ------------------
  |  Branch (78:20): [True: 642M, False: 18.2k]
  ------------------
   79|   642M|      hash ^= static_cast<unsigned char>(byte);
   80|   642M|      hash *= prime;
   81|   642M|    }
   82|       |
   83|  18.2k|    return hash;
   84|  18.2k|  }
_ZN10FNV1A_hash14fnv1ahash_implIKDsLm18446744073709551615EQaaaa11is_hashableIT_Entsr3stdE9is_same_vIS2_cEntsr3stdE9is_same_vIS2_hEEEmNSt3__14spanIS2_XT0_EEE:
   89|  16.0k|  static constexpr std::uint64_t fnv1ahash_impl(std::span<Basic, N> data) {
   90|  16.0k|    return fnv1ahash_impl({reinterpret_cast<const unsigned char*>(data.data()),
   91|  16.0k|                           data.size_bytes()});
   92|  16.0k|  }
_ZN10FNV1A_hash14fnv1ahash_implENSt3__14spanIKhLm18446744073709551615EEE:
   65|  25.5k|  fnv1ahash_impl(std::span<const unsigned char> bytes) {
   66|  25.5k|    auto hash = offset;
   67|       |
   68|  2.53G|    for (std::uint64_t byte : bytes) {
  ------------------
  |  Branch (68:29): [True: 2.53G, False: 25.5k]
  ------------------
   69|  2.53G|      hash ^= byte;
   70|  2.53G|      hash *= prime;
   71|  2.53G|    }
   72|       |
   73|  25.5k|    return hash;
   74|  25.5k|  }
_Z29get_supported_implementationsv:
   26|  36.5k|get_supported_implementations() {
   27|  36.5k|  static const auto impl = []() -> auto {
   28|  36.5k|    std::vector<const simdutf::implementation*> ret;
   29|  36.5k|    for (auto e : simdutf::get_available_implementations()) {
   30|  36.5k|      std::cerr << "implementation " << e->name() << " is available? "
   31|  36.5k|                << e->supported_by_runtime_system() << '\n';
   32|  36.5k|      if (e->supported_by_runtime_system()) {
   33|  36.5k|        ret.push_back(e);
   34|  36.5k|      }
   35|  36.5k|    }
   36|  36.5k|    return ret;
   37|  36.5k|  }();
   38|  36.5k|  return {impl.data(), impl.size()};
   39|  36.5k|}
_ZZ29get_supported_implementationsvENKUlvE_clEv:
   27|      1|  static const auto impl = []() -> auto {
   28|      1|    std::vector<const simdutf::implementation*> ret;
   29|      4|    for (auto e : simdutf::get_available_implementations()) {
  ------------------
  |  Branch (29:17): [True: 4, False: 1]
  ------------------
   30|      4|      std::cerr << "implementation " << e->name() << " is available? "
   31|      4|                << e->supported_by_runtime_system() << '\n';
   32|      4|      if (e->supported_by_runtime_system()) {
  ------------------
  |  Branch (32:11): [True: 3, False: 1]
  ------------------
   33|      3|        ret.push_back(e);
   34|      3|      }
   35|      4|    }
   36|      1|    return ret;
   37|      1|  }();
_ZneRKN7simdutf6resultES2_:
   42|  32.1k|inline bool operator!=(const simdutf::result& a, const simdutf::result& b) {
   43|  32.1k|  return a.count != b.count || a.error != b.error;
  ------------------
  |  Branch (43:10): [True: 0, False: 32.1k]
  |  Branch (43:32): [True: 0, False: 32.1k]
  ------------------
   44|  32.1k|}
_ZN10FNV1A_hash6as_strIJNSt3__16vectorIDiNS1_9allocatorIDiEEEEEEENS1_12basic_stringIcNS1_11char_traitsIcEENS3_IcEEEEDpRKT_:
  100|  9.42k|  template <typename... Data> static std::string as_str(const Data&... data) {
  101|  9.42k|    static_assert(sizeof...(Data) > 0, "must hash with at least one argument");
  102|  9.42k|    std::uint64_t h;
  103|       |    if constexpr (sizeof...(Data) > 1) {
  104|       |      const std::array hashes{fnv1ahash_impl(data)...};
  105|       |      const auto s = std::span(hashes);
  106|       |      h = fnv1ahash_impl(s);
  107|  9.42k|    } else {
  108|  9.42k|      h = fnv1ahash_impl(data...);
  109|  9.42k|    }
  110|  9.42k|    constexpr std::size_t expected_chars = 16;
  111|  9.42k|    std::string ret(expected_chars, '0');
  112|  9.42k|    auto c = std::to_chars(ret.data(), ret.data() + ret.size(), h, 16);
  113|  9.42k|    assert(c.ec == std::errc{});
  ------------------
  |  Branch (113:5): [True: 9.42k, False: 0]
  ------------------
  114|  9.42k|    auto nwritten = c.ptr - ret.data();
  115|  9.42k|    assert(nwritten <= expected_chars);
  ------------------
  |  Branch (115:5): [True: 9.42k, False: 0]
  ------------------
  116|  9.42k|    std::rotate(ret.data(), c.ptr, ret.data() + expected_chars);
  117|  9.42k|    return ret;
  118|  9.42k|  }
_ZN10FNV1A_hash14fnv1ahash_implIDiQ11is_hashableIT_EEEmRKNSt3__16vectorIS1_NS2_9allocatorIS1_EEEE:
   96|  9.42k|  static constexpr std::uint64_t fnv1ahash_impl(const std::vector<Data>& data) {
   97|  9.42k|    return fnv1ahash_impl(std::span(data));
   98|  9.42k|  }
_ZN10FNV1A_hash14fnv1ahash_implIKDiLm18446744073709551615EQaaaa11is_hashableIT_Entsr3stdE9is_same_vIS2_cEntsr3stdE9is_same_vIS2_hEEEmNSt3__14spanIS2_XT0_EEE:
   89|  9.42k|  static constexpr std::uint64_t fnv1ahash_impl(std::span<Basic, N> data) {
   90|  9.42k|    return fnv1ahash_impl({reinterpret_cast<const unsigned char*>(data.data()),
   91|  9.42k|                           data.size_bytes()});
   92|  9.42k|  }
_ZN10FNV1A_hash6as_strIJNSt3__16vectorIcNS1_9allocatorIcEEEEEEENS1_12basic_stringIcNS1_11char_traitsIcEES4_EEDpRKT_:
  100|  18.2k|  template <typename... Data> static std::string as_str(const Data&... data) {
  101|  18.2k|    static_assert(sizeof...(Data) > 0, "must hash with at least one argument");
  102|  18.2k|    std::uint64_t h;
  103|       |    if constexpr (sizeof...(Data) > 1) {
  104|       |      const std::array hashes{fnv1ahash_impl(data)...};
  105|       |      const auto s = std::span(hashes);
  106|       |      h = fnv1ahash_impl(s);
  107|  18.2k|    } else {
  108|  18.2k|      h = fnv1ahash_impl(data...);
  109|  18.2k|    }
  110|  18.2k|    constexpr std::size_t expected_chars = 16;
  111|  18.2k|    std::string ret(expected_chars, '0');
  112|  18.2k|    auto c = std::to_chars(ret.data(), ret.data() + ret.size(), h, 16);
  113|  18.2k|    assert(c.ec == std::errc{});
  ------------------
  |  Branch (113:5): [True: 18.2k, False: 0]
  ------------------
  114|  18.2k|    auto nwritten = c.ptr - ret.data();
  115|  18.2k|    assert(nwritten <= expected_chars);
  ------------------
  |  Branch (115:5): [True: 18.2k, False: 0]
  ------------------
  116|  18.2k|    std::rotate(ret.data(), c.ptr, ret.data() + expected_chars);
  117|  18.2k|    return ret;
  118|  18.2k|  }
_ZN10FNV1A_hash14fnv1ahash_implIcQ11is_hashableIT_EEEmRKNSt3__16vectorIS1_NS2_9allocatorIS1_EEEE:
   96|  18.2k|  static constexpr std::uint64_t fnv1ahash_impl(const std::vector<Data>& data) {
   97|  18.2k|    return fnv1ahash_impl(std::span(data));
   98|  18.2k|  }
_ZN10FNV1A_hash6as_strIJNSt3__16vectorIDsNS1_9allocatorIDsEEEEEEENS1_12basic_stringIcNS1_11char_traitsIcEENS3_IcEEEEDpRKT_:
  100|  16.0k|  template <typename... Data> static std::string as_str(const Data&... data) {
  101|  16.0k|    static_assert(sizeof...(Data) > 0, "must hash with at least one argument");
  102|  16.0k|    std::uint64_t h;
  103|       |    if constexpr (sizeof...(Data) > 1) {
  104|       |      const std::array hashes{fnv1ahash_impl(data)...};
  105|       |      const auto s = std::span(hashes);
  106|       |      h = fnv1ahash_impl(s);
  107|  16.0k|    } else {
  108|  16.0k|      h = fnv1ahash_impl(data...);
  109|  16.0k|    }
  110|  16.0k|    constexpr std::size_t expected_chars = 16;
  111|  16.0k|    std::string ret(expected_chars, '0');
  112|  16.0k|    auto c = std::to_chars(ret.data(), ret.data() + ret.size(), h, 16);
  113|  16.0k|    assert(c.ec == std::errc{});
  ------------------
  |  Branch (113:5): [True: 16.0k, False: 0]
  ------------------
  114|  16.0k|    auto nwritten = c.ptr - ret.data();
  115|  16.0k|    assert(nwritten <= expected_chars);
  ------------------
  |  Branch (115:5): [True: 16.0k, False: 0]
  ------------------
  116|  16.0k|    std::rotate(ret.data(), c.ptr, ret.data() + expected_chars);
  117|  16.0k|    return ret;
  118|  16.0k|  }
_ZN10FNV1A_hash14fnv1ahash_implIDsQ11is_hashableIT_EEEmRKNSt3__16vectorIS1_NS2_9allocatorIS1_EEEE:
   96|  16.0k|  static constexpr std::uint64_t fnv1ahash_impl(const std::vector<Data>& data) {
   97|  16.0k|    return fnv1ahash_impl(std::span(data));
   98|  16.0k|  }
_ZeqRKN7simdutf6resultES2_:
   45|  8.61k|inline bool operator==(const simdutf::result& a, const simdutf::result& b) {
   46|  8.61k|  return a.count == b.count && a.error == b.error;
  ------------------
  |  Branch (46:10): [True: 8.61k, False: 0]
  |  Branch (46:32): [True: 8.61k, False: 0]
  ------------------
   47|  8.61k|}

_ZNK23ValidationFunctionTraitIL12UtfEncodings0EE24ValidationWithErrorsNameMUlvE_clEv:
 1178|      2|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|      2|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|      2|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|      2|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|      2|  constexpr auto _size = _name.size();                                \
 1183|      2|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|      2|  return _nameof; }()
_ZNK6nameof7cstringILt28EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|      4|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt28EE4dataEv:
  233|      4|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt28EE4sizeEv:
  235|      4|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
_ZNK23ValidationFunctionTraitIL12UtfEncodings1EE24ValidationWithErrorsNameMUlvE_clEv:
 1178|      2|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|      2|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|      2|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|      2|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|      2|  constexpr auto _size = _name.size();                                \
 1183|      2|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|      2|  return _nameof; }()
_ZNK23ValidationFunctionTraitIL12UtfEncodings3EE24ValidationWithErrorsNameMUlvE_clEv:
 1178|      2|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|      2|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|      2|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|      2|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|      2|  constexpr auto _size = _name.size();                                \
 1183|      2|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|      2|  return _nameof; }()
_ZNK6nameof7cstringILt26EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|      2|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt26EE4dataEv:
  233|      2|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt26EE4sizeEv:
  235|      2|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
_ZNK23ValidationFunctionTraitIL12UtfEncodings2EE24ValidationWithErrorsNameMUlvE_clEv:
 1178|      2|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|      2|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|      2|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|      2|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|      2|  constexpr auto _size = _name.size();                                \
 1183|      2|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|      2|  return _nameof; }()
_ZNK6nameof7cstringILt25EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|  1.07k|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt25EE4dataEv:
  233|  1.07k|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt25EE4sizeEv:
  235|  1.07k|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK3$_0clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    107|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    107|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    107|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    107|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    107|  constexpr auto _size = _name.size();                                \
 1183|    107|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    107|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_0clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    107|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    107|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    107|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    107|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    107|  constexpr auto _size = _name.size();                                \
 1183|    107|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    107|  return _nameof; }()
_ZNK6nameof7cstringILt30EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|    413|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt30EE4dataEv:
  233|    413|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt30EE4sizeEv:
  235|    413|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK3$_1clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    247|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    247|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    247|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    247|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    247|  constexpr auto _size = _name.size();                                \
 1183|    247|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    247|  return _nameof; }()
_ZNK6nameof7cstringILt24EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|  3.11k|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt24EE4dataEv:
  233|  3.11k|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt24EE4sizeEv:
  235|  3.11k|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK3$_1clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    247|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    247|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    247|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    247|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    247|  constexpr auto _size = _name.size();                                \
 1183|    247|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    247|  return _nameof; }()
_ZNK6nameof7cstringILt29EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|    931|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt29EE4dataEv:
  233|    931|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt29EE4sizeEv:
  235|    931|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK3$_2clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     70|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     70|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     70|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     70|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     70|  constexpr auto _size = _name.size();                                \
 1183|     70|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     70|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_2clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     70|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     70|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     70|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     70|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     70|  constexpr auto _size = _name.size();                                \
 1183|     70|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     70|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_3clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    214|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    214|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    214|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    214|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    214|  constexpr auto _size = _name.size();                                \
 1183|    214|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    214|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_3clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    214|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    214|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    214|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    214|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    214|  constexpr auto _size = _name.size();                                \
 1183|    214|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    214|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_4clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     71|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     71|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     71|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     71|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     71|  constexpr auto _size = _name.size();                                \
 1183|     71|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     71|  return _nameof; }()
_ZNK6nameof7cstringILt23EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|  3.98k|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt23EE4dataEv:
  233|  3.98k|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt23EE4sizeEv:
  235|  3.98k|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK3$_4clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     71|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     71|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     71|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     71|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     71|  constexpr auto _size = _name.size();                                \
 1183|     71|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     71|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_5clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    165|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    165|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    165|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    165|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    165|  constexpr auto _size = _name.size();                                \
 1183|    165|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    165|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_5clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    165|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    165|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    165|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    165|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    165|  constexpr auto _size = _name.size();                                \
 1183|    165|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    165|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_6clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    118|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    118|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    118|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    118|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    118|  constexpr auto _size = _name.size();                                \
 1183|    118|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    118|  return _nameof; }()
_ZNK6nameof7cstringILt22EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|  5.03k|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt22EE4dataEv:
  233|  5.03k|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt22EE4sizeEv:
  235|  5.03k|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK3$_6clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    118|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    118|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    118|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    118|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    118|  constexpr auto _size = _name.size();                                \
 1183|    118|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    118|  return _nameof; }()
_ZNK6nameof7cstringILt27EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|    377|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt27EE4dataEv:
  233|    377|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt27EE4sizeEv:
  235|    377|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK3$_7clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    264|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    264|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    264|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    264|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    264|  constexpr auto _size = _name.size();                                \
 1183|    264|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    264|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_7clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    264|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    264|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    264|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    264|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    264|  constexpr auto _size = _name.size();                                \
 1183|    264|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    264|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_8clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    206|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    206|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    206|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    206|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    206|  constexpr auto _size = _name.size();                                \
 1183|    206|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    206|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_8clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    206|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    206|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    206|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    206|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    206|  constexpr auto _size = _name.size();                                \
 1183|    206|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    206|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_9clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    259|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    259|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    259|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    259|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    259|  constexpr auto _size = _name.size();                                \
 1183|    259|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    259|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_9clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    259|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    259|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    259|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    259|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    259|  constexpr auto _size = _name.size();                                \
 1183|    259|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    259|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_10clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     49|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     49|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     49|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     49|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     49|  constexpr auto _size = _name.size();                                \
 1183|     49|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     49|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_10clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     49|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     49|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     49|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     49|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     49|  constexpr auto _size = _name.size();                                \
 1183|     49|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     49|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_11clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    139|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    139|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    139|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    139|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    139|  constexpr auto _size = _name.size();                                \
 1183|    139|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    139|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_11clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    139|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    139|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    139|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    139|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    139|  constexpr auto _size = _name.size();                                \
 1183|    139|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    139|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_12clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    198|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    198|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    198|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    198|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    198|  constexpr auto _size = _name.size();                                \
 1183|    198|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    198|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_12clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    198|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    198|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    198|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    198|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    198|  constexpr auto _size = _name.size();                                \
 1183|    198|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    198|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_13clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     60|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     60|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     60|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     60|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     60|  constexpr auto _size = _name.size();                                \
 1183|     60|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     60|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_13clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     60|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     60|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     60|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     60|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     60|  constexpr auto _size = _name.size();                                \
 1183|     60|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     60|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_14clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    151|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    151|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    151|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    151|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    151|  constexpr auto _size = _name.size();                                \
 1183|    151|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    151|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_14clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    151|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    151|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    151|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    151|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    151|  constexpr auto _size = _name.size();                                \
 1183|    151|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    151|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_15clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    236|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    236|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    236|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    236|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    236|  constexpr auto _size = _name.size();                                \
 1183|    236|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    236|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_15clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    236|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    236|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    236|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    236|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    236|  constexpr auto _size = _name.size();                                \
 1183|    236|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    236|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_16clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     93|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     93|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     93|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     93|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     93|  constexpr auto _size = _name.size();                                \
 1183|     93|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     93|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_16clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     93|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     93|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     93|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     93|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     93|  constexpr auto _size = _name.size();                                \
 1183|     93|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     93|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_17clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    244|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    244|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    244|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    244|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    244|  constexpr auto _size = _name.size();                                \
 1183|    244|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    244|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_17clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    244|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    244|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    244|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    244|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    244|  constexpr auto _size = _name.size();                                \
 1183|    244|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    244|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_18clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    243|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    243|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    243|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    243|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    243|  constexpr auto _size = _name.size();                                \
 1183|    243|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    243|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_18clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    243|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    243|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    243|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    243|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    243|  constexpr auto _size = _name.size();                                \
 1183|    243|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    243|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_19clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    363|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    363|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    363|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    363|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    363|  constexpr auto _size = _name.size();                                \
 1183|    363|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    363|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_19clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    363|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    363|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    363|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    363|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    363|  constexpr auto _size = _name.size();                                \
 1183|    363|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    363|  return _nameof; }()
_ZNK6nameof7cstringILt21EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|    841|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt21EE4dataEv:
  233|    841|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt21EE4sizeEv:
  235|    841|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK4$_20clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    355|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    355|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    355|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    355|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    355|  constexpr auto _size = _name.size();                                \
 1183|    355|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    355|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_20clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    355|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    355|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    355|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    355|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    355|  constexpr auto _size = _name.size();                                \
 1183|    355|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    355|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_21clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    504|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    504|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    504|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    504|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    504|  constexpr auto _size = _name.size();                                \
 1183|    504|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    504|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_21clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    504|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    504|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    504|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    504|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    504|  constexpr auto _size = _name.size();                                \
 1183|    504|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    504|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_22clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    475|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    475|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    475|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    475|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    475|  constexpr auto _size = _name.size();                                \
 1183|    475|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    475|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_22clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    475|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    475|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    475|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    475|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    475|  constexpr auto _size = _name.size();                                \
 1183|    475|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    475|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_23clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    478|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    478|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    478|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    478|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    478|  constexpr auto _size = _name.size();                                \
 1183|    478|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    478|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_23clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    478|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    478|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    478|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    478|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    478|  constexpr auto _size = _name.size();                                \
 1183|    478|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    478|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_24clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    124|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    124|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    124|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    124|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    124|  constexpr auto _size = _name.size();                                \
 1183|    124|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    124|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_24clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    124|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    124|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    124|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    124|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    124|  constexpr auto _size = _name.size();                                \
 1183|    124|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    124|  return _nameof; }()
_ZNK6nameof7cstringILt37EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|    264|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt37EE4dataEv:
  233|    264|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt37EE4sizeEv:
  235|    264|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK4$_25clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    180|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    180|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    180|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    180|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    180|  constexpr auto _size = _name.size();                                \
 1183|    180|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    180|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_25clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    180|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    180|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    180|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    180|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    180|  constexpr auto _size = _name.size();                                \
 1183|    180|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    180|  return _nameof; }()
_ZNK6nameof7cstringILt36EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|  1.11k|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt36EE4dataEv:
  233|  1.11k|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt36EE4sizeEv:
  235|  1.11k|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK4$_26clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    278|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    278|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    278|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    278|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    278|  constexpr auto _size = _name.size();                                \
 1183|    278|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    278|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_26clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    278|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    278|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    278|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    278|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    278|  constexpr auto _size = _name.size();                                \
 1183|    278|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    278|  return _nameof; }()
_ZNK6nameof7cstringILt35EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|  1.67k|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt35EE4dataEv:
  233|  1.67k|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt35EE4sizeEv:
  235|  1.67k|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK4$_27clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    140|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    140|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    140|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    140|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    140|  constexpr auto _size = _name.size();                                \
 1183|    140|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    140|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_27clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    140|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    140|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    140|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    140|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    140|  constexpr auto _size = _name.size();                                \
 1183|    140|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    140|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_28clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    221|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    221|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    221|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    221|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    221|  constexpr auto _size = _name.size();                                \
 1183|    221|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    221|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_28clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    221|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    221|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    221|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    221|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    221|  constexpr auto _size = _name.size();                                \
 1183|    221|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    221|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_29clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    343|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    343|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    343|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    343|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    343|  constexpr auto _size = _name.size();                                \
 1183|    343|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    343|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_29clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    343|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    343|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    343|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    343|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    343|  constexpr auto _size = _name.size();                                \
 1183|    343|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    343|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_30clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    210|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    210|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    210|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    210|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    210|  constexpr auto _size = _name.size();                                \
 1183|    210|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    210|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_30clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    210|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    210|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    210|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    210|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    210|  constexpr auto _size = _name.size();                                \
 1183|    210|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    210|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_31clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    335|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    335|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    335|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    335|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    335|  constexpr auto _size = _name.size();                                \
 1183|    335|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    335|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_31clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    335|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    335|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    335|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    335|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    335|  constexpr auto _size = _name.size();                                \
 1183|    335|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    335|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_32clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    374|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    374|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    374|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    374|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    374|  constexpr auto _size = _name.size();                                \
 1183|    374|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    374|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_32clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    374|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    374|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    374|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    374|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    374|  constexpr auto _size = _name.size();                                \
 1183|    374|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    374|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_33clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    432|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    432|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    432|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    432|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    432|  constexpr auto _size = _name.size();                                \
 1183|    432|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    432|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_33clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    432|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    432|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    432|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    432|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    432|  constexpr auto _size = _name.size();                                \
 1183|    432|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    432|  return _nameof; }()
_ZNK6nameof7cstringILt33EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|    895|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt33EE4dataEv:
  233|    895|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt33EE4sizeEv:
  235|    895|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK4$_34clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    365|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    365|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    365|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    365|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    365|  constexpr auto _size = _name.size();                                \
 1183|    365|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    365|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_34clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    365|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    365|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    365|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    365|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    365|  constexpr auto _size = _name.size();                                \
 1183|    365|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    365|  return _nameof; }()
_ZNK6nameof7cstringILt34EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|    365|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt34EE4dataEv:
  233|    365|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt34EE4sizeEv:
  235|    365|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK4$_35clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    467|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    467|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    467|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    467|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    467|  constexpr auto _size = _name.size();                                \
 1183|    467|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    467|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_35clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    467|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    467|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    467|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    467|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    467|  constexpr auto _size = _name.size();                                \
 1183|    467|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    467|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_36clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    373|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    373|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    373|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    373|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    373|  constexpr auto _size = _name.size();                                \
 1183|    373|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    373|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_36clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    373|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    373|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    373|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    373|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    373|  constexpr auto _size = _name.size();                                \
 1183|    373|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    373|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_37clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    463|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    463|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    463|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    463|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    463|  constexpr auto _size = _name.size();                                \
 1183|    463|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    463|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_37clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    463|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    463|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    463|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    463|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    463|  constexpr auto _size = _name.size();                                \
 1183|    463|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    463|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_38clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     48|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     48|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     48|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     48|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     48|  constexpr auto _size = _name.size();                                \
 1183|     48|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     48|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_38clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     48|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     48|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     48|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     48|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     48|  constexpr auto _size = _name.size();                                \
 1183|     48|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     48|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_39clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     55|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     55|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     55|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     55|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     55|  constexpr auto _size = _name.size();                                \
 1183|     55|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     55|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_39clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     55|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     55|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     55|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     55|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     55|  constexpr auto _size = _name.size();                                \
 1183|     55|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     55|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_40clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     38|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     38|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     38|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     38|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     38|  constexpr auto _size = _name.size();                                \
 1183|     38|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     38|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_40clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     38|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     38|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     38|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     38|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     38|  constexpr auto _size = _name.size();                                \
 1183|     38|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     38|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_41clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    280|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    280|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    280|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    280|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    280|  constexpr auto _size = _name.size();                                \
 1183|    280|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    280|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_41clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    280|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    280|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    280|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    280|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    280|  constexpr auto _size = _name.size();                                \
 1183|    280|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    280|  return _nameof; }()

_ZN7simdutf6resultC2ENS_10error_codeEm:
   87|  81.0k|      : error{err}, count{pos} {}
_ZNK7simdutf6result6is_errEv:
   93|  5.44k|  simdutf_really_inline simdutf_constexpr23 bool is_err() const noexcept {
   94|  5.44k|    return error != error_code::SUCCESS;
   95|  5.44k|  }
_ZN7simdutf11full_resultC2ENS_10error_codeEmm:
  111|  5.71k|      : error{err}, input_count{pos_in}, output_count{pos_out} {}
_ZNK7simdutf11full_resultcvNS_6resultEEv:
  117|  3.72k|  simdutf_really_inline simdutf_constexpr23 operator result() const noexcept {
  118|  3.72k|    if (error == error_code::SUCCESS) {
  ------------------
  |  Branch (118:9): [True: 2.34k, False: 1.38k]
  ------------------
  119|  2.34k|      return result{error, output_count};
  120|  2.34k|    } else {
  121|  1.38k|      return result{error, input_count};
  122|  1.38k|    }
  123|  3.72k|  }

_ZNK7simdutf14implementation4nameEv:
 5562|      4|  virtual std::string_view name() const noexcept { return _name; }
_ZNK7simdutf14implementation25required_instruction_setsEv:
 5613|      8|  virtual uint32_t required_instruction_sets() const {
 5614|      8|    return _required_instruction_sets;
 5615|      8|  }
_ZNK7simdutf14implementation24utf16_length_from_latin1Em:
 6818|    279|  utf16_length_from_latin1(size_t length) const noexcept {
 6819|    279|    return length;
 6820|    279|  }
_ZNK7simdutf14implementation24latin1_length_from_utf32Em:
 7006|    909|  latin1_length_from_utf32(size_t length) const noexcept {
 7007|    909|    return length;
 7008|    909|  }
_ZNK7simdutf14implementation24latin1_length_from_utf16Em:
 7043|  1.11k|  latin1_length_from_utf16(size_t length) const noexcept {
 7044|  1.11k|    return length;
 7045|  1.11k|  }
_ZNK7simdutf14implementation24utf32_length_from_latin1Em:
 7076|    144|  utf32_length_from_latin1(size_t length) const noexcept {
 7077|    144|    return length;
 7078|    144|  }
_ZN7simdutf14implementationC2EPKcS2_j:
 7514|      4|      : _name(name), _description(description),
 7515|      4|        _required_instruction_sets(required_instruction_sets) {}
_ZN7simdutf8internal29available_implementation_listC2Ev:
 7546|      1|  simdutf_really_inline available_implementation_list() {}

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

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

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115latin1_to_utf167convertILNS_10endiannessE0EPKcQsr7simdutf6detailE22indexes_into_byte_likeIT0_EEEmS7_mPDs:
   14|    192|                                   char16_t *utf16_output) {
   15|    192|  size_t pos = 0;
   16|    192|  char16_t *start{utf16_output};
   17|       |
   18|  2.07M|  while (pos < len) {
  ------------------
  |  Branch (18:10): [True: 2.07M, False: 192]
  ------------------
   19|  2.07M|    uint16_t word =
   20|  2.07M|        uint8_t(data[pos]); // extend Latin-1 char to 16-bit Unicode code point
   21|  2.07M|    *utf16_output++ =
   22|  2.07M|        char16_t(match_system(big_endian) ? word : u16_swap_bytes(word));
  ------------------
  |  Branch (22:18): [True: 2.07M, Folded]
  ------------------
   23|  2.07M|    pos++;
   24|  2.07M|  }
   25|       |
   26|    192|  return utf16_output - start;
   27|    192|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115latin1_to_utf167convertILNS_10endiannessE1EPKcQsr7simdutf6detailE22indexes_into_byte_likeIT0_EEEmS7_mPDs:
   14|    286|                                   char16_t *utf16_output) {
   15|    286|  size_t pos = 0;
   16|    286|  char16_t *start{utf16_output};
   17|       |
   18|  2.29M|  while (pos < len) {
  ------------------
  |  Branch (18:10): [True: 2.29M, False: 286]
  ------------------
   19|  2.29M|    uint16_t word =
   20|  2.29M|        uint8_t(data[pos]); // extend Latin-1 char to 16-bit Unicode code point
   21|  2.29M|    *utf16_output++ =
   22|  2.29M|        char16_t(match_system(big_endian) ? word : u16_swap_bytes(word));
  ------------------
  |  Branch (22:18): [Folded, False: 2.29M]
  ------------------
   23|  2.29M|    pos++;
   24|  2.29M|  }
   25|       |
   26|    286|  return utf16_output - start;
   27|    286|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115latin1_to_utf327convertIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEEmS6_mPDi:
   14|    224|                                   char32_t *utf32_output) {
   15|    224|  char32_t *start{utf32_output};
   16|  5.10M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (16:22): [True: 5.10M, False: 224]
  ------------------
   17|  5.10M|    *utf32_output++ = uint8_t(data[i]);
   18|  5.10M|  }
   19|    224|  return utf32_output - start;
   20|    224|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf87convertEPKcmPc:
   66|  1.57k|                                     char *utf8_output) {
   67|  1.57k|  return convert(reinterpret_cast<const unsigned char *>(buf), len,
   68|  1.57k|                 utf8_output);
   69|  1.57k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf87convertIPKhPcQaasr7simdutf6detailE22indexes_into_byte_likeIT_Esr7simdutf6detailE26index_assignable_from_charIT0_EEEmS7_mS8_:
   17|  1.57k|                                   OutputPtr utf8_output) {
   18|       |  // const unsigned char *data = reinterpret_cast<const unsigned char *>(buf);
   19|  1.57k|  size_t pos = 0;
   20|  1.57k|  size_t utf8_pos = 0;
   21|       |
   22|  6.41M|  while (pos < len) {
  ------------------
  |  Branch (22:10): [True: 6.40M, False: 1.57k]
  ------------------
   23|       |#if SIMDUTF_CPLUSPLUS23
   24|       |    if !consteval
   25|       |#endif
   26|  6.40M|    {
   27|       |      // try to convert the next block of 16 ASCII bytes
   28|  6.40M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (28:11): [True: 6.39M, False: 14.0k]
  ------------------
   29|       |                             // they are ascii
   30|  6.39M|        uint64_t v1;
   31|  6.39M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
   32|  6.39M|        uint64_t v2;
   33|  6.39M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   34|  6.39M|        uint64_t v{v1 |
   35|  6.39M|                   v2}; // We are only interested in these bits: 1000 1000 1000
   36|       |                        // 1000, so it makes sense to concatenate everything
   37|  6.39M|        if ((v & 0x8080808080808080) ==
  ------------------
  |  Branch (37:13): [True: 2.84M, False: 3.55M]
  ------------------
   38|  6.39M|            0) { // if NONE of these are set, e.g. all of them are zero, then
   39|       |                 // everything is ASCII
   40|  2.84M|          size_t final_pos = pos + 16;
   41|  48.2M|          while (pos < final_pos) {
  ------------------
  |  Branch (41:18): [True: 45.4M, False: 2.84M]
  ------------------
   42|  45.4M|            utf8_output[utf8_pos++] = char(data[pos]);
   43|  45.4M|            pos++;
   44|  45.4M|          }
   45|  2.84M|          continue;
   46|  2.84M|        }
   47|  6.39M|      } // if (pos + 16 <= len)
   48|  6.40M|    } // !consteval scope
   49|       |
   50|  3.56M|    unsigned char byte = data[pos];
   51|  3.56M|    if ((byte & 0x80) == 0) { // if ASCII
  ------------------
  |  Branch (51:9): [True: 2.25M, False: 1.31M]
  ------------------
   52|       |      // will generate one UTF-8 bytes
   53|  2.25M|      utf8_output[utf8_pos++] = char(byte);
   54|  2.25M|      pos++;
   55|  2.25M|    } else {
   56|       |      // will generate two UTF-8 bytes
   57|  1.31M|      utf8_output[utf8_pos++] = char((byte >> 6) | 0b11000000);
   58|  1.31M|      utf8_output[utf8_pos++] = char((byte & 0b111111) | 0b10000000);
   59|  1.31M|      pos++;
   60|  1.31M|    }
   61|  3.56M|  } // while
   62|  1.57k|  return utf8_pos;
   63|  1.57k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf823utf8_length_from_latin1IPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEEmS6_m:
  199|    280|utf8_length_from_latin1(InputPtr input, size_t length) noexcept {
  200|    280|  size_t answer = length;
  201|    280|  size_t i = 0;
  202|       |
  203|       |#if SIMDUTF_CPLUSPLUS23
  204|       |  if !consteval
  205|       |#endif
  206|    280|  {
  207|    280|    auto pop = [](uint64_t v) {
  208|    280|      return (size_t)(((v >> 7) & UINT64_C(0x0101010101010101)) *
  209|    280|                          UINT64_C(0x0101010101010101) >>
  210|    280|                      56);
  211|    280|    };
  212|   765k|    for (; i + 32 <= length; i += 32) {
  ------------------
  |  Branch (212:12): [True: 765k, False: 280]
  ------------------
  213|   765k|      uint64_t v;
  214|   765k|      memcpy(&v, input + i, 8);
  215|   765k|      answer += pop(v);
  216|   765k|      memcpy(&v, input + i + 8, sizeof(v));
  217|   765k|      answer += pop(v);
  218|   765k|      memcpy(&v, input + i + 16, sizeof(v));
  219|   765k|      answer += pop(v);
  220|   765k|      memcpy(&v, input + i + 24, sizeof(v));
  221|   765k|      answer += pop(v);
  222|   765k|    }
  223|    569|    for (; i + 8 <= length; i += 8) {
  ------------------
  |  Branch (223:12): [True: 289, False: 280]
  ------------------
  224|    289|      uint64_t v;
  225|    289|      memcpy(&v, input + i, sizeof(v));
  226|    289|      answer += pop(v);
  227|    289|    }
  228|    280|  } // !consteval scope
  229|    862|  for (; i + 1 <= length; i += 1) {
  ------------------
  |  Branch (229:10): [True: 582, False: 280]
  ------------------
  230|    582|    answer += static_cast<uint8_t>(input[i]) >> 7;
  231|    582|  }
  232|    280|  return answer;
  233|    280|}
simdutf.cpp:_ZZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf823utf8_length_from_latin1IPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEEmS6_mENKUlmE_clEm:
  207|  3.06M|    auto pop = [](uint64_t v) {
  208|  3.06M|      return (size_t)(((v >> 7) & UINT64_C(0x0101010101010101)) *
  209|       |                          UINT64_C(0x0101010101010101) >>
  210|  3.06M|                      56);
  211|  3.06M|    };

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

_ZN7simdutf6scalar5utf168validateILNS_10endiannessE0EEEbPKDsm:
   22|  4.03k|validate(const char16_t *data, size_t len) noexcept {
   23|  4.03k|  uint64_t pos = 0;
   24|  21.8M|  while (pos < len) {
  ------------------
  |  Branch (24:10): [True: 21.8M, False: 2.85k]
  ------------------
   25|  21.8M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(data[pos]);
   26|  21.8M|    if ((word & 0xF800) == 0xD800) {
  ------------------
  |  Branch (26:9): [True: 20.8k, False: 21.7M]
  ------------------
   27|  20.8k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (27:11): [True: 234, False: 20.5k]
  ------------------
   28|    234|        return false;
   29|    234|      }
   30|  20.5k|      char16_t diff = char16_t(word - 0xD800);
   31|  20.5k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (31:11): [True: 383, False: 20.1k]
  ------------------
   32|    383|        return false;
   33|    383|      }
   34|  20.1k|      char16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (34:28): [Folded, False: 20.1k]
  ------------------
   35|  20.1k|                               ? u16_swap_bytes(data[pos + 1])
   36|  20.1k|                               : data[pos + 1];
   37|  20.1k|      char16_t diff2 = char16_t(next_word - 0xDC00);
   38|  20.1k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (38:11): [True: 564, False: 19.6k]
  ------------------
   39|    564|        return false;
   40|    564|      }
   41|  19.6k|      pos += 2;
   42|  21.7M|    } else {
   43|  21.7M|      pos++;
   44|  21.7M|    }
   45|  21.8M|  }
   46|  2.85k|  return true;
   47|  4.03k|}
_ZN7simdutf6scalar5utf168validateILNS_10endiannessE1EEEbPKDsm:
   22|  3.70k|validate(const char16_t *data, size_t len) noexcept {
   23|  3.70k|  uint64_t pos = 0;
   24|  21.1M|  while (pos < len) {
  ------------------
  |  Branch (24:10): [True: 21.1M, False: 2.55k]
  ------------------
   25|  21.1M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(data[pos]);
   26|  21.1M|    if ((word & 0xF800) == 0xD800) {
  ------------------
  |  Branch (26:9): [True: 25.4k, False: 21.1M]
  ------------------
   27|  25.4k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (27:11): [True: 237, False: 25.2k]
  ------------------
   28|    237|        return false;
   29|    237|      }
   30|  25.2k|      char16_t diff = char16_t(word - 0xD800);
   31|  25.2k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (31:11): [True: 355, False: 24.8k]
  ------------------
   32|    355|        return false;
   33|    355|      }
   34|  24.8k|      char16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (34:28): [True: 24.8k, Folded]
  ------------------
   35|  24.8k|                               ? u16_swap_bytes(data[pos + 1])
   36|  24.8k|                               : data[pos + 1];
   37|  24.8k|      char16_t diff2 = char16_t(next_word - 0xDC00);
   38|  24.8k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (38:11): [True: 563, False: 24.2k]
  ------------------
   39|    563|        return false;
   40|    563|      }
   41|  24.2k|      pos += 2;
   42|  21.1M|    } else {
   43|  21.1M|      pos++;
   44|  21.1M|    }
   45|  21.1M|  }
   46|  2.55k|  return true;
   47|  3.70k|}
_ZN7simdutf6scalar5utf1620validate_with_errorsILNS_10endiannessE0EEENS_6resultEPKDsm:
   51|  4.27k|validate_with_errors(const char16_t *data, size_t len) noexcept {
   52|  4.27k|  size_t pos = 0;
   53|  21.8M|  while (pos < len) {
  ------------------
  |  Branch (53:10): [True: 21.8M, False: 2.85k]
  ------------------
   54|  21.8M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(data[pos]);
   55|  21.8M|    if ((word & 0xF800) == 0xD800) {
  ------------------
  |  Branch (55:9): [True: 21.0k, False: 21.7M]
  ------------------
   56|  21.0k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (56:11): [True: 234, False: 20.8k]
  ------------------
   57|    234|        return result(error_code::SURROGATE, pos);
   58|    234|      }
   59|  20.8k|      char16_t diff = char16_t(word - 0xD800);
   60|  20.8k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (60:11): [True: 504, False: 20.3k]
  ------------------
   61|    504|        return result(error_code::SURROGATE, pos);
   62|    504|      }
   63|  20.3k|      char16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (63:28): [Folded, False: 20.3k]
  ------------------
   64|  20.3k|                               ? u16_swap_bytes(data[pos + 1])
   65|  20.3k|                               : data[pos + 1];
   66|  20.3k|      char16_t diff2 = uint16_t(next_word - 0xDC00);
   67|  20.3k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (67:11): [True: 681, False: 19.6k]
  ------------------
   68|    681|        return result(error_code::SURROGATE, pos);
   69|    681|      }
   70|  19.6k|      pos += 2;
   71|  21.7M|    } else {
   72|  21.7M|      pos++;
   73|  21.7M|    }
   74|  21.8M|  }
   75|  2.85k|  return result(error_code::SUCCESS, pos);
   76|  4.27k|}
_ZN7simdutf6scalar5utf1620validate_with_errorsILNS_10endiannessE1EEENS_6resultEPKDsm:
   51|  3.93k|validate_with_errors(const char16_t *data, size_t len) noexcept {
   52|  3.93k|  size_t pos = 0;
   53|  21.1M|  while (pos < len) {
  ------------------
  |  Branch (53:10): [True: 21.1M, False: 2.55k]
  ------------------
   54|  21.1M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(data[pos]);
   55|  21.1M|    if ((word & 0xF800) == 0xD800) {
  ------------------
  |  Branch (55:9): [True: 25.7k, False: 21.1M]
  ------------------
   56|  25.7k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (56:11): [True: 237, False: 25.4k]
  ------------------
   57|    237|        return result(error_code::SURROGATE, pos);
   58|    237|      }
   59|  25.4k|      char16_t diff = char16_t(word - 0xD800);
   60|  25.4k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (60:11): [True: 468, False: 25.0k]
  ------------------
   61|    468|        return result(error_code::SURROGATE, pos);
   62|    468|      }
   63|  25.0k|      char16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (63:28): [True: 25.0k, Folded]
  ------------------
   64|  25.0k|                               ? u16_swap_bytes(data[pos + 1])
   65|  25.0k|                               : data[pos + 1];
   66|  25.0k|      char16_t diff2 = uint16_t(next_word - 0xDC00);
   67|  25.0k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (67:11): [True: 675, False: 24.3k]
  ------------------
   68|    675|        return result(error_code::SURROGATE, pos);
   69|    675|      }
   70|  24.3k|      pos += 2;
   71|  21.1M|    } else {
   72|  21.1M|      pos++;
   73|  21.1M|    }
   74|  21.1M|  }
   75|  2.55k|  return result(error_code::SUCCESS, pos);
   76|  3.93k|}
_ZN7simdutf6scalar5utf1617count_code_pointsILNS_10endiannessE0EEEmPKDsm:
   79|  5.18k|simdutf_constexpr23 size_t count_code_points(const char16_t *p, size_t len) {
   80|       |  // We are not BOM aware.
   81|  5.18k|  size_t counter{0};
   82|  26.7M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (82:22): [True: 26.6M, False: 5.18k]
  ------------------
   83|  26.6M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
   84|  26.6M|    counter += ((word & 0xFC00) != 0xDC00);
   85|  26.6M|  }
   86|  5.18k|  return counter;
   87|  5.18k|}
_ZN7simdutf6scalar5utf1617count_code_pointsILNS_10endiannessE1EEEmPKDsm:
   79|  4.81k|simdutf_constexpr23 size_t count_code_points(const char16_t *p, size_t len) {
   80|       |  // We are not BOM aware.
   81|  4.81k|  size_t counter{0};
   82|  28.0M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (82:22): [True: 28.0M, False: 4.81k]
  ------------------
   83|  28.0M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
   84|  28.0M|    counter += ((word & 0xFC00) != 0xDC00);
   85|  28.0M|  }
   86|  4.81k|  return counter;
   87|  4.81k|}
_ZN7simdutf6scalar5utf1622utf8_length_from_utf16ILNS_10endiannessE0EEEmPKDsm:
   91|  2.37k|                                                  size_t len) {
   92|       |  // We are not BOM aware.
   93|  2.37k|  size_t counter{0};
   94|  17.3M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (94:22): [True: 17.3M, False: 2.37k]
  ------------------
   95|  17.3M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
   96|  17.3M|    counter++; // ASCII
   97|  17.3M|    counter += static_cast<size_t>(
   98|  17.3M|        word >
   99|  17.3M|        0x7F); // non-ASCII is at least 2 bytes, surrogates are 2*2 == 4 bytes
  100|  17.3M|    counter += static_cast<size_t>((word > 0x7FF && word <= 0xD7FF) ||
  ------------------
  |  Branch (100:37): [True: 4.85M, False: 12.5M]
  |  Branch (100:53): [True: 4.57M, False: 281k]
  ------------------
  101|  12.7M|                                   (word >= 0xE000)); // three-byte
  ------------------
  |  Branch (101:36): [True: 226k, False: 12.5M]
  ------------------
  102|  17.3M|  }
  103|  2.37k|  return counter;
  104|  2.37k|}
_ZN7simdutf6scalar5utf1622utf8_length_from_utf16ILNS_10endiannessE1EEEmPKDsm:
   91|  2.16k|                                                  size_t len) {
   92|       |  // We are not BOM aware.
   93|  2.16k|  size_t counter{0};
   94|  15.2M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (94:22): [True: 15.2M, False: 2.16k]
  ------------------
   95|  15.2M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
   96|  15.2M|    counter++; // ASCII
   97|  15.2M|    counter += static_cast<size_t>(
   98|  15.2M|        word >
   99|  15.2M|        0x7F); // non-ASCII is at least 2 bytes, surrogates are 2*2 == 4 bytes
  100|  15.2M|    counter += static_cast<size_t>((word > 0x7FF && word <= 0xD7FF) ||
  ------------------
  |  Branch (100:37): [True: 2.90M, False: 12.2M]
  |  Branch (100:53): [True: 2.76M, False: 139k]
  ------------------
  101|  12.4M|                                   (word >= 0xE000)); // three-byte
  ------------------
  |  Branch (101:36): [True: 113k, False: 12.3M]
  ------------------
  102|  15.2M|  }
  103|  2.16k|  return counter;
  104|  2.16k|}
_ZN7simdutf6scalar5utf1623utf32_length_from_utf16ILNS_10endiannessE0EEEmPKDsm:
  108|    442|                                                   size_t len) {
  109|       |  // We are not BOM aware.
  110|    442|  size_t counter{0};
  111|  4.13M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (111:22): [True: 4.13M, False: 442]
  ------------------
  112|  4.13M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
  113|  4.13M|    counter += ((word & 0xFC00) != 0xDC00);
  114|  4.13M|  }
  115|    442|  return counter;
  116|    442|}
_ZN7simdutf6scalar5utf1623utf32_length_from_utf16ILNS_10endiannessE1EEEmPKDsm:
  108|    426|                                                   size_t len) {
  109|       |  // We are not BOM aware.
  110|    426|  size_t counter{0};
  111|  8.23M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (111:22): [True: 8.23M, False: 426]
  ------------------
  112|  8.23M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
  113|  8.23M|    counter += ((word & 0xFC00) != 0xDC00);
  114|  8.23M|  }
  115|    426|  return counter;
  116|    426|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115utf16_to_latin17convertILNS_10endiannessE0EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT0_Esr7simdutf6detailE26index_assignable_from_charIT1_EEEmS8_mS9_:
   17|    256|                                   OutputPtr latin_output) {
   18|    256|  if (len == 0) {
  ------------------
  |  Branch (18:7): [True: 6, False: 250]
  ------------------
   19|      6|    return 0;
   20|      6|  }
   21|    250|  size_t pos = 0;
   22|    250|  const auto latin_output_start = latin_output;
   23|    250|  uint16_t word = 0;
   24|    250|  uint16_t too_large = 0;
   25|       |
   26|  2.64M|  while (pos < len) {
  ------------------
  |  Branch (26:10): [True: 2.64M, False: 250]
  ------------------
   27|  2.64M|    word = !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (27:12): [Folded, False: 2.64M]
  ------------------
   28|  2.64M|    too_large |= word;
   29|  2.64M|    *latin_output++ = char(word & 0xFF);
   30|  2.64M|    pos++;
   31|  2.64M|  }
   32|    250|  if ((too_large & 0xFF00) != 0) {
  ------------------
  |  Branch (32:7): [True: 152, False: 98]
  ------------------
   33|    152|    return 0;
   34|    152|  }
   35|       |
   36|     98|  return latin_output - latin_output_start;
   37|    250|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115utf16_to_latin17convertILNS_10endiannessE1EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT0_Esr7simdutf6detailE26index_assignable_from_charIT1_EEEmS8_mS9_:
   17|    230|                                   OutputPtr latin_output) {
   18|    230|  if (len == 0) {
  ------------------
  |  Branch (18:7): [True: 6, False: 224]
  ------------------
   19|      6|    return 0;
   20|      6|  }
   21|    224|  size_t pos = 0;
   22|    224|  const auto latin_output_start = latin_output;
   23|    224|  uint16_t word = 0;
   24|    224|  uint16_t too_large = 0;
   25|       |
   26|  7.57k|  while (pos < len) {
  ------------------
  |  Branch (26:10): [True: 7.34k, False: 224]
  ------------------
   27|  7.34k|    word = !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (27:12): [True: 7.34k, Folded]
  ------------------
   28|  7.34k|    too_large |= word;
   29|  7.34k|    *latin_output++ = char(word & 0xFF);
   30|  7.34k|    pos++;
   31|  7.34k|  }
   32|    224|  if ((too_large & 0xFF00) != 0) {
  ------------------
  |  Branch (32:7): [True: 114, False: 110]
  ------------------
   33|    114|    return 0;
   34|    114|  }
   35|       |
   36|    110|  return latin_output - latin_output_start;
   37|    224|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115utf16_to_latin119convert_with_errorsILNS_10endiannessE0EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT0_Esr7simdutf6detailE26index_assignable_from_charIT1_EEENS_6resultES8_mS9_:
   45|    510|                                               OutputPtr latin_output) {
   46|    510|  if (len == 0) {
  ------------------
  |  Branch (46:7): [True: 4, False: 506]
  ------------------
   47|      4|    return result(error_code::SUCCESS, 0);
   48|      4|  }
   49|    506|  size_t pos = 0;
   50|    506|  auto start = latin_output;
   51|    506|  uint16_t word;
   52|       |
   53|   421k|  while (pos < len) {
  ------------------
  |  Branch (53:10): [True: 420k, False: 234]
  ------------------
   54|       |#if SIMDUTF_CPLUSPLUS23
   55|       |    if !consteval
   56|       |#endif
   57|   420k|    {
   58|   420k|      if (pos + 16 <= len) { // if it is safe to read 32 more bytes, check that
  ------------------
  |  Branch (58:11): [True: 418k, False: 2.11k]
  ------------------
   59|       |                             // they are Latin1
   60|   418k|        uint64_t v1, v2, v3, v4;
   61|   418k|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
   62|   418k|        ::memcpy(&v2, data + pos + 4, sizeof(uint64_t));
   63|   418k|        ::memcpy(&v3, data + pos + 8, sizeof(uint64_t));
   64|   418k|        ::memcpy(&v4, data + pos + 12, sizeof(uint64_t));
   65|       |
   66|       |        if constexpr (!match_system(big_endian)) {
   67|       |          v1 = (v1 >> 8) | (v1 << (64 - 8));
   68|       |        }
   69|       |        if constexpr (!match_system(big_endian)) {
   70|       |          v2 = (v2 >> 8) | (v2 << (64 - 8));
   71|       |        }
   72|       |        if constexpr (!match_system(big_endian)) {
   73|       |          v3 = (v3 >> 8) | (v3 << (64 - 8));
   74|       |        }
   75|       |        if constexpr (!match_system(big_endian)) {
   76|       |          v4 = (v4 >> 8) | (v4 << (64 - 8));
   77|       |        }
   78|       |
   79|   418k|        if (((v1 | v2 | v3 | v4) & 0xFF00FF00FF00FF00) == 0) {
  ------------------
  |  Branch (79:13): [True: 418k, False: 248]
  ------------------
   80|   418k|          size_t final_pos = pos + 16;
   81|  7.11M|          while (pos < final_pos) {
  ------------------
  |  Branch (81:18): [True: 6.69M, False: 418k]
  ------------------
   82|  6.69M|            *latin_output++ = !match_system(big_endian)
  ------------------
  |  Branch (82:31): [Folded, False: 6.69M]
  ------------------
   83|  6.69M|                                  ? char(u16_swap_bytes(data[pos]))
   84|  6.69M|                                  : char(data[pos]);
   85|  6.69M|            pos++;
   86|  6.69M|          }
   87|   418k|          continue;
   88|   418k|        }
   89|   418k|      }
   90|   420k|    }
   91|       |
   92|  2.36k|    word = !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (92:12): [Folded, False: 2.36k]
  ------------------
   93|  2.36k|    if ((word & 0xFF00) == 0) {
  ------------------
  |  Branch (93:9): [True: 2.09k, False: 272]
  ------------------
   94|  2.09k|      *latin_output++ = char(word & 0xFF);
   95|  2.09k|      pos++;
   96|  2.09k|    } else {
   97|    272|      return result(error_code::TOO_LARGE, pos);
   98|    272|    }
   99|  2.36k|  }
  100|    234|  return result(error_code::SUCCESS, latin_output - start);
  101|    506|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115utf16_to_latin119convert_with_errorsILNS_10endiannessE1EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT0_Esr7simdutf6detailE26index_assignable_from_charIT1_EEENS_6resultES8_mS9_:
   45|    450|                                               OutputPtr latin_output) {
   46|    450|  if (len == 0) {
  ------------------
  |  Branch (46:7): [True: 4, False: 446]
  ------------------
   47|      4|    return result(error_code::SUCCESS, 0);
   48|      4|  }
   49|    446|  size_t pos = 0;
   50|    446|  auto start = latin_output;
   51|    446|  uint16_t word;
   52|       |
   53|   458k|  while (pos < len) {
  ------------------
  |  Branch (53:10): [True: 458k, False: 190]
  ------------------
   54|       |#if SIMDUTF_CPLUSPLUS23
   55|       |    if !consteval
   56|       |#endif
   57|   458k|    {
   58|   458k|      if (pos + 16 <= len) { // if it is safe to read 32 more bytes, check that
  ------------------
  |  Branch (58:11): [True: 457k, False: 1.44k]
  ------------------
   59|       |                             // they are Latin1
   60|   457k|        uint64_t v1, v2, v3, v4;
   61|   457k|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
   62|   457k|        ::memcpy(&v2, data + pos + 4, sizeof(uint64_t));
   63|   457k|        ::memcpy(&v3, data + pos + 8, sizeof(uint64_t));
   64|   457k|        ::memcpy(&v4, data + pos + 12, sizeof(uint64_t));
   65|       |
   66|   457k|        if constexpr (!match_system(big_endian)) {
   67|   457k|          v1 = (v1 >> 8) | (v1 << (64 - 8));
   68|   457k|        }
   69|   457k|        if constexpr (!match_system(big_endian)) {
   70|   457k|          v2 = (v2 >> 8) | (v2 << (64 - 8));
   71|   457k|        }
   72|   457k|        if constexpr (!match_system(big_endian)) {
   73|   457k|          v3 = (v3 >> 8) | (v3 << (64 - 8));
   74|   457k|        }
   75|   457k|        if constexpr (!match_system(big_endian)) {
   76|   457k|          v4 = (v4 >> 8) | (v4 << (64 - 8));
   77|   457k|        }
   78|       |
   79|   457k|        if (((v1 | v2 | v3 | v4) & 0xFF00FF00FF00FF00) == 0) {
  ------------------
  |  Branch (79:13): [True: 456k, False: 320]
  ------------------
   80|   456k|          size_t final_pos = pos + 16;
   81|  7.76M|          while (pos < final_pos) {
  ------------------
  |  Branch (81:18): [True: 7.31M, False: 456k]
  ------------------
   82|  7.31M|            *latin_output++ = !match_system(big_endian)
  ------------------
  |  Branch (82:31): [True: 7.31M, Folded]
  ------------------
   83|  7.31M|                                  ? char(u16_swap_bytes(data[pos]))
   84|  7.31M|                                  : char(data[pos]);
   85|  7.31M|            pos++;
   86|  7.31M|          }
   87|   456k|          continue;
   88|   456k|        }
   89|   457k|      }
   90|   458k|    }
   91|       |
   92|  1.76k|    word = !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (92:12): [True: 1.76k, Folded]
  ------------------
   93|  1.76k|    if ((word & 0xFF00) == 0) {
  ------------------
  |  Branch (93:9): [True: 1.51k, False: 256]
  ------------------
   94|  1.51k|      *latin_output++ = char(word & 0xFF);
   95|  1.51k|      pos++;
   96|  1.51k|    } else {
   97|    256|      return result(error_code::TOO_LARGE, pos);
   98|    256|    }
   99|  1.76k|  }
  100|    190|  return result(error_code::SUCCESS, latin_output - start);
  101|    446|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf16_to_utf327convertILNS_10endiannessE0EEEmPKDsmPDi:
   11|    844|                                   char32_t *utf32_output) {
   12|    844|  size_t pos = 0;
   13|    844|  char32_t *start{utf32_output};
   14|   214k|  while (pos < len) {
  ------------------
  |  Branch (14:10): [True: 214k, False: 508]
  ------------------
   15|   214k|    uint16_t word =
   16|   214k|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (16:9): [Folded, False: 214k]
  ------------------
   17|   214k|    if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (17:9): [True: 210k, False: 3.39k]
  ------------------
   18|       |      // No surrogate pair, extend 16-bit word to 32-bit word
   19|   210k|      *utf32_output++ = char32_t(word);
   20|   210k|      pos++;
   21|   210k|    } else {
   22|       |      // must be a surrogate pair
   23|  3.39k|      uint16_t diff = uint16_t(word - 0xD800);
   24|  3.39k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (24:11): [True: 98, False: 3.29k]
  ------------------
   25|     98|        return 0;
   26|     98|      }
   27|  3.29k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (27:11): [True: 84, False: 3.21k]
  ------------------
   28|     84|        return 0;
   29|     84|      } // minimal bound checking
   30|  3.21k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (30:28): [Folded, False: 3.21k]
  ------------------
   31|  3.21k|                               ? u16_swap_bytes(data[pos + 1])
   32|  3.21k|                               : data[pos + 1];
   33|  3.21k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   34|  3.21k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (34:11): [True: 154, False: 3.05k]
  ------------------
   35|    154|        return 0;
   36|    154|      }
   37|  3.05k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   38|  3.05k|      *utf32_output++ = char32_t(value);
   39|  3.05k|      pos += 2;
   40|  3.05k|    }
   41|   214k|  }
   42|    508|  return utf32_output - start;
   43|    844|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf16_to_utf327convertILNS_10endiannessE1EEEmPKDsmPDi:
   11|    880|                                   char32_t *utf32_output) {
   12|    880|  size_t pos = 0;
   13|    880|  char32_t *start{utf32_output};
   14|  2.21M|  while (pos < len) {
  ------------------
  |  Branch (14:10): [True: 2.20M, False: 564]
  ------------------
   15|  2.20M|    uint16_t word =
   16|  2.20M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (16:9): [True: 2.20M, Folded]
  ------------------
   17|  2.20M|    if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (17:9): [True: 2.19M, False: 10.7k]
  ------------------
   18|       |      // No surrogate pair, extend 16-bit word to 32-bit word
   19|  2.19M|      *utf32_output++ = char32_t(word);
   20|  2.19M|      pos++;
   21|  2.19M|    } else {
   22|       |      // must be a surrogate pair
   23|  10.7k|      uint16_t diff = uint16_t(word - 0xD800);
   24|  10.7k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (24:11): [True: 92, False: 10.6k]
  ------------------
   25|     92|        return 0;
   26|     92|      }
   27|  10.6k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (27:11): [True: 84, False: 10.5k]
  ------------------
   28|     84|        return 0;
   29|     84|      } // minimal bound checking
   30|  10.5k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (30:28): [True: 10.5k, Folded]
  ------------------
   31|  10.5k|                               ? u16_swap_bytes(data[pos + 1])
   32|  10.5k|                               : data[pos + 1];
   33|  10.5k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   34|  10.5k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (34:11): [True: 140, False: 10.3k]
  ------------------
   35|    140|        return 0;
   36|    140|      }
   37|  10.3k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   38|  10.3k|      *utf32_output++ = char32_t(value);
   39|  10.3k|      pos += 2;
   40|  10.3k|    }
   41|  2.20M|  }
   42|    564|  return utf32_output - start;
   43|    880|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf16_to_utf3219convert_with_errorsILNS_10endiannessE0EEENS_6resultEPKDsmPDi:
   47|    942|                                               char32_t *utf32_output) {
   48|    942|  size_t pos = 0;
   49|    942|  char32_t *start{utf32_output};
   50|  3.22M|  while (pos < len) {
  ------------------
  |  Branch (50:10): [True: 3.22M, False: 516]
  ------------------
   51|  3.22M|    uint16_t word =
   52|  3.22M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (52:9): [Folded, False: 3.22M]
  ------------------
   53|  3.22M|    if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (53:9): [True: 3.21M, False: 11.4k]
  ------------------
   54|       |      // No surrogate pair, extend 16-bit word to 32-bit word
   55|  3.21M|      *utf32_output++ = char32_t(word);
   56|  3.21M|      pos++;
   57|  3.21M|    } else {
   58|       |      // must be a surrogate pair
   59|  11.4k|      uint16_t diff = uint16_t(word - 0xD800);
   60|  11.4k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (60:11): [True: 142, False: 11.2k]
  ------------------
   61|    142|        return result(error_code::SURROGATE, pos);
   62|    142|      }
   63|  11.2k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (63:11): [True: 114, False: 11.1k]
  ------------------
   64|    114|        return result(error_code::SURROGATE, pos);
   65|    114|      } // minimal bound checking
   66|  11.1k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (66:28): [Folded, False: 11.1k]
  ------------------
   67|  11.1k|                               ? u16_swap_bytes(data[pos + 1])
   68|  11.1k|                               : data[pos + 1];
   69|  11.1k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   70|  11.1k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (70:11): [True: 170, False: 10.9k]
  ------------------
   71|    170|        return result(error_code::SURROGATE, pos);
   72|    170|      }
   73|  10.9k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   74|  10.9k|      *utf32_output++ = char32_t(value);
   75|  10.9k|      pos += 2;
   76|  10.9k|    }
   77|  3.22M|  }
   78|    516|  return result(error_code::SUCCESS, utf32_output - start);
   79|    942|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf16_to_utf3219convert_with_errorsILNS_10endiannessE1EEENS_6resultEPKDsmPDi:
   47|    798|                                               char32_t *utf32_output) {
   48|    798|  size_t pos = 0;
   49|    798|  char32_t *start{utf32_output};
   50|  1.73M|  while (pos < len) {
  ------------------
  |  Branch (50:10): [True: 1.73M, False: 414]
  ------------------
   51|  1.73M|    uint16_t word =
   52|  1.73M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (52:9): [True: 1.73M, Folded]
  ------------------
   53|  1.73M|    if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (53:9): [True: 1.72M, False: 11.2k]
  ------------------
   54|       |      // No surrogate pair, extend 16-bit word to 32-bit word
   55|  1.72M|      *utf32_output++ = char32_t(word);
   56|  1.72M|      pos++;
   57|  1.72M|    } else {
   58|       |      // must be a surrogate pair
   59|  11.2k|      uint16_t diff = uint16_t(word - 0xD800);
   60|  11.2k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (60:11): [True: 108, False: 11.1k]
  ------------------
   61|    108|        return result(error_code::SURROGATE, pos);
   62|    108|      }
   63|  11.1k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (63:11): [True: 108, False: 11.0k]
  ------------------
   64|    108|        return result(error_code::SURROGATE, pos);
   65|    108|      } // minimal bound checking
   66|  11.0k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (66:28): [True: 11.0k, Folded]
  ------------------
   67|  11.0k|                               ? u16_swap_bytes(data[pos + 1])
   68|  11.0k|                               : data[pos + 1];
   69|  11.0k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   70|  11.0k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (70:11): [True: 168, False: 10.8k]
  ------------------
   71|    168|        return result(error_code::SURROGATE, pos);
   72|    168|      }
   73|  10.8k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   74|  10.8k|      *utf32_output++ = char32_t(value);
   75|  10.8k|      pos += 2;
   76|  10.8k|    }
   77|  1.73M|  }
   78|    414|  return result(error_code::SUCCESS, utf32_output - start);
   79|    798|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf16_to_utf3213convert_validILNS_10endiannessE0EEEmPKDsmPDi:
   11|    128|                                         char32_t *utf32_output) {
   12|    128|  size_t pos = 0;
   13|    128|  char32_t *start{utf32_output};
   14|  2.96M|  while (pos < len) {
  ------------------
  |  Branch (14:10): [True: 2.96M, False: 128]
  ------------------
   15|  2.96M|    uint16_t word =
   16|  2.96M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (16:9): [Folded, False: 2.96M]
  ------------------
   17|  2.96M|    if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (17:9): [True: 2.95M, False: 4.90k]
  ------------------
   18|       |      // No surrogate pair, extend 16-bit word to 32-bit word
   19|  2.95M|      *utf32_output++ = char32_t(word);
   20|  2.95M|      pos++;
   21|  2.95M|    } else {
   22|       |      // must be a surrogate pair
   23|  4.90k|      uint16_t diff = uint16_t(word - 0xD800);
   24|  4.90k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (24:11): [True: 0, False: 4.90k]
  ------------------
   25|      0|        return 0;
   26|      0|      } // minimal bound checking
   27|  4.90k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (27:28): [Folded, False: 4.90k]
  ------------------
   28|  4.90k|                               ? u16_swap_bytes(data[pos + 1])
   29|  4.90k|                               : data[pos + 1];
   30|  4.90k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   31|  4.90k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   32|  4.90k|      *utf32_output++ = char32_t(value);
   33|  4.90k|      pos += 2;
   34|  4.90k|    }
   35|  2.96M|  }
   36|    128|  return utf32_output - start;
   37|    128|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf16_to_utf3213convert_validILNS_10endiannessE1EEEmPKDsmPDi:
   11|    180|                                         char32_t *utf32_output) {
   12|    180|  size_t pos = 0;
   13|    180|  char32_t *start{utf32_output};
   14|  5.40M|  while (pos < len) {
  ------------------
  |  Branch (14:10): [True: 5.40M, False: 180]
  ------------------
   15|  5.40M|    uint16_t word =
   16|  5.40M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (16:9): [True: 5.40M, Folded]
  ------------------
   17|  5.40M|    if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (17:9): [True: 5.40M, False: 1.90k]
  ------------------
   18|       |      // No surrogate pair, extend 16-bit word to 32-bit word
   19|  5.40M|      *utf32_output++ = char32_t(word);
   20|  5.40M|      pos++;
   21|  5.40M|    } else {
   22|       |      // must be a surrogate pair
   23|  1.90k|      uint16_t diff = uint16_t(word - 0xD800);
   24|  1.90k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (24:11): [True: 0, False: 1.90k]
  ------------------
   25|      0|        return 0;
   26|      0|      } // minimal bound checking
   27|  1.90k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (27:28): [True: 1.90k, Folded]
  ------------------
   28|  1.90k|                               ? u16_swap_bytes(data[pos + 1])
   29|  1.90k|                               : data[pos + 1];
   30|  1.90k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   31|  1.90k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   32|  1.90k|      *utf32_output++ = char32_t(value);
   33|  1.90k|      pos += 2;
   34|  1.90k|    }
   35|  5.40M|  }
   36|    180|  return utf32_output - start;
   37|    180|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf87convertILNS_10endiannessE0EPKDsPcQsr7simdutf6detailE18indexes_into_utf16IT0_EEEmS8_mT1_:
   17|  1.95k|                                   OutputPtr utf8_output) {
   18|  1.95k|  size_t pos = 0;
   19|  1.95k|  const auto start = utf8_output;
   20|  3.13M|  while (pos < len) {
  ------------------
  |  Branch (20:10): [True: 3.13M, False: 1.64k]
  ------------------
   21|       |#if SIMDUTF_CPLUSPLUS23
   22|       |    if !consteval
   23|       |#endif
   24|  3.13M|    {
   25|       |      // try to convert the next block of 8 bytes
   26|  3.13M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (26:11): [True: 3.13M, False: 3.65k]
  ------------------
   27|       |                            // they are ascii
   28|  3.13M|        uint64_t v;
   29|  3.13M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   30|       |        if constexpr (!match_system(big_endian)) {
   31|       |          v = (v >> 8) | (v << (64 - 8));
   32|       |        }
   33|  3.13M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (33:13): [True: 1.87M, False: 1.25M]
  ------------------
   34|  1.87M|          size_t final_pos = pos + 4;
   35|  9.38M|          while (pos < final_pos) {
  ------------------
  |  Branch (35:18): [True: 7.50M, False: 1.87M]
  ------------------
   36|  7.50M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (36:30): [Folded, False: 7.50M]
  ------------------
   37|  7.50M|                                 ? char(u16_swap_bytes(data[pos]))
   38|  7.50M|                                 : char(data[pos]);
   39|  7.50M|            pos++;
   40|  7.50M|          }
   41|  1.87M|          continue;
   42|  1.87M|        }
   43|  3.13M|      }
   44|  3.13M|    }
   45|  1.26M|    uint16_t word =
   46|  1.26M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (46:9): [Folded, False: 1.26M]
  ------------------
   47|  1.26M|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (47:9): [True: 278k, False: 981k]
  ------------------
   48|       |      // will generate one UTF-8 bytes
   49|   278k|      *utf8_output++ = char(word);
   50|   278k|      pos++;
   51|   981k|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (51:16): [True: 169k, False: 811k]
  ------------------
   52|       |      // will generate two UTF-8 bytes
   53|       |      // we have 0b110XXXXX 0b10XXXXXX
   54|   169k|      *utf8_output++ = char((word >> 6) | 0b11000000);
   55|   169k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   56|   169k|      pos++;
   57|   811k|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (57:16): [True: 806k, False: 5.47k]
  ------------------
   58|       |      // will generate three UTF-8 bytes
   59|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
   60|   806k|      *utf8_output++ = char((word >> 12) | 0b11100000);
   61|   806k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   62|   806k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   63|   806k|      pos++;
   64|   806k|    } else {
   65|       |      // must be a surrogate pair
   66|  5.47k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (66:11): [True: 42, False: 5.43k]
  ------------------
   67|     42|        return 0;
   68|     42|      }
   69|  5.43k|      uint16_t diff = uint16_t(word - 0xD800);
   70|  5.43k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (70:11): [True: 130, False: 5.30k]
  ------------------
   71|    130|        return 0;
   72|    130|      }
   73|  5.30k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (73:28): [Folded, False: 5.30k]
  ------------------
   74|  5.30k|                               ? u16_swap_bytes(data[pos + 1])
   75|  5.30k|                               : data[pos + 1];
   76|  5.30k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   77|  5.30k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (77:11): [True: 146, False: 5.15k]
  ------------------
   78|    146|        return 0;
   79|    146|      }
   80|  5.15k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   81|       |      // will generate four UTF-8 bytes
   82|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
   83|  5.15k|      *utf8_output++ = char((value >> 18) | 0b11110000);
   84|  5.15k|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
   85|  5.15k|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
   86|  5.15k|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
   87|  5.15k|      pos += 2;
   88|  5.15k|    }
   89|  1.26M|  }
   90|  1.64k|  return utf8_output - start;
   91|  1.95k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf87convertILNS_10endiannessE1EPKDsPcQsr7simdutf6detailE18indexes_into_utf16IT0_EEEmS8_mT1_:
   17|  1.86k|                                   OutputPtr utf8_output) {
   18|  1.86k|  size_t pos = 0;
   19|  1.86k|  const auto start = utf8_output;
   20|  3.10M|  while (pos < len) {
  ------------------
  |  Branch (20:10): [True: 3.10M, False: 1.53k]
  ------------------
   21|       |#if SIMDUTF_CPLUSPLUS23
   22|       |    if !consteval
   23|       |#endif
   24|  3.10M|    {
   25|       |      // try to convert the next block of 8 bytes
   26|  3.10M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (26:11): [True: 3.10M, False: 3.35k]
  ------------------
   27|       |                            // they are ascii
   28|  3.10M|        uint64_t v;
   29|  3.10M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   30|  3.10M|        if constexpr (!match_system(big_endian)) {
   31|  3.10M|          v = (v >> 8) | (v << (64 - 8));
   32|  3.10M|        }
   33|  3.10M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (33:13): [True: 1.30M, False: 1.79M]
  ------------------
   34|  1.30M|          size_t final_pos = pos + 4;
   35|  6.51M|          while (pos < final_pos) {
  ------------------
  |  Branch (35:18): [True: 5.21M, False: 1.30M]
  ------------------
   36|  5.21M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (36:30): [True: 5.21M, Folded]
  ------------------
   37|  5.21M|                                 ? char(u16_swap_bytes(data[pos]))
   38|  5.21M|                                 : char(data[pos]);
   39|  5.21M|            pos++;
   40|  5.21M|          }
   41|  1.30M|          continue;
   42|  1.30M|        }
   43|  3.10M|      }
   44|  3.10M|    }
   45|  1.80M|    uint16_t word =
   46|  1.80M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (46:9): [True: 1.80M, Folded]
  ------------------
   47|  1.80M|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (47:9): [True: 180k, False: 1.62M]
  ------------------
   48|       |      // will generate one UTF-8 bytes
   49|   180k|      *utf8_output++ = char(word);
   50|   180k|      pos++;
   51|  1.62M|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (51:16): [True: 53.2k, False: 1.56M]
  ------------------
   52|       |      // will generate two UTF-8 bytes
   53|       |      // we have 0b110XXXXX 0b10XXXXXX
   54|  53.2k|      *utf8_output++ = char((word >> 6) | 0b11000000);
   55|  53.2k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   56|  53.2k|      pos++;
   57|  1.56M|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (57:16): [True: 1.56M, False: 6.85k]
  ------------------
   58|       |      // will generate three UTF-8 bytes
   59|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
   60|  1.56M|      *utf8_output++ = char((word >> 12) | 0b11100000);
   61|  1.56M|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   62|  1.56M|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   63|  1.56M|      pos++;
   64|  1.56M|    } else {
   65|       |      // must be a surrogate pair
   66|  6.85k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (66:11): [True: 42, False: 6.81k]
  ------------------
   67|     42|        return 0;
   68|     42|      }
   69|  6.81k|      uint16_t diff = uint16_t(word - 0xD800);
   70|  6.81k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (70:11): [True: 144, False: 6.66k]
  ------------------
   71|    144|        return 0;
   72|    144|      }
   73|  6.66k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (73:28): [True: 6.66k, Folded]
  ------------------
   74|  6.66k|                               ? u16_swap_bytes(data[pos + 1])
   75|  6.66k|                               : data[pos + 1];
   76|  6.66k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   77|  6.66k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (77:11): [True: 150, False: 6.51k]
  ------------------
   78|    150|        return 0;
   79|    150|      }
   80|  6.51k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   81|       |      // will generate four UTF-8 bytes
   82|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
   83|  6.51k|      *utf8_output++ = char((value >> 18) | 0b11110000);
   84|  6.51k|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
   85|  6.51k|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
   86|  6.51k|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
   87|  6.51k|      pos += 2;
   88|  6.51k|    }
   89|  1.80M|  }
   90|  1.53k|  return utf8_output - start;
   91|  1.86k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf819convert_with_errorsILNS_10endiannessE0ELb0EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT1_Esr7simdutf6detailE26index_assignable_from_charIT2_EEENS_11full_resultES8_mS9_m:
  101|  1.84k|                                                    size_t utf8_len = 0) {
  102|  1.84k|  if (check_output && utf8_len == 0) {
  ------------------
  |  Branch (102:7): [Folded, False: 1.84k]
  |  Branch (102:23): [True: 0, False: 0]
  ------------------
  103|      0|    return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, 0, 0);
  104|      0|  }
  105|       |
  106|  1.84k|  size_t pos = 0;
  107|  1.84k|  auto start = utf8_output;
  108|  1.84k|  auto end = utf8_output + utf8_len;
  109|       |
  110|  7.64M|  while (pos < len) {
  ------------------
  |  Branch (110:10): [True: 7.64M, False: 1.37k]
  ------------------
  111|       |#if SIMDUTF_CPLUSPLUS23
  112|       |    if !consteval
  113|       |#endif
  114|  7.64M|    {
  115|       |      // try to convert the next block of 8 bytes
  116|  7.64M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (116:11): [True: 7.64M, False: 3.18k]
  ------------------
  117|       |                            // they are ascii
  118|  7.64M|        uint64_t v;
  119|  7.64M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
  120|       |        if constexpr (!match_system(big_endian))
  121|       |          v = (v >> 8) | (v << (64 - 8));
  122|  7.64M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (122:13): [True: 1.37M, False: 6.26M]
  ------------------
  123|  1.37M|          size_t final_pos = pos + 4;
  124|  6.85M|          while (pos < final_pos) {
  ------------------
  |  Branch (124:18): [True: 5.48M, False: 1.37M]
  ------------------
  125|  5.48M|            if (check_output && size_t(end - utf8_output) < 1) {
  ------------------
  |  Branch (125:17): [Folded, False: 5.48M]
  |  Branch (125:33): [True: 0, False: 0]
  ------------------
  126|      0|              return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  127|      0|                                 utf8_output - start);
  128|      0|            }
  129|  5.48M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (129:30): [Folded, False: 5.48M]
  ------------------
  130|  5.48M|                                 ? char(u16_swap_bytes(data[pos]))
  131|  5.48M|                                 : char(data[pos]);
  132|  5.48M|            pos++;
  133|  5.48M|          }
  134|  1.37M|          continue;
  135|  1.37M|        }
  136|  7.64M|      }
  137|  7.64M|    }
  138|       |
  139|  6.27M|    uint16_t word =
  140|  6.27M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (140:9): [Folded, False: 6.27M]
  ------------------
  141|  6.27M|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (141:9): [True: 340k, False: 5.93M]
  ------------------
  142|       |      // will generate one UTF-8 bytes
  143|   340k|      if (check_output && size_t(end - utf8_output) < 1) {
  ------------------
  |  Branch (143:11): [Folded, False: 340k]
  |  Branch (143:27): [True: 0, False: 0]
  ------------------
  144|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  145|      0|                           utf8_output - start);
  146|      0|      }
  147|   340k|      *utf8_output++ = char(word);
  148|   340k|      pos++;
  149|  5.93M|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (149:16): [True: 784k, False: 5.14M]
  ------------------
  150|       |      // will generate two UTF-8 bytes
  151|       |      // we have 0b110XXXXX 0b10XXXXXX
  152|   784k|      if (check_output && size_t(end - utf8_output) < 2) {
  ------------------
  |  Branch (152:11): [Folded, False: 784k]
  |  Branch (152:27): [True: 0, False: 0]
  ------------------
  153|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  154|      0|                           utf8_output - start);
  155|      0|      }
  156|   784k|      *utf8_output++ = char((word >> 6) | 0b11000000);
  157|   784k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  158|   784k|      pos++;
  159|       |
  160|  5.14M|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (160:16): [True: 5.14M, False: 6.91k]
  ------------------
  161|       |      // will generate three UTF-8 bytes
  162|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
  163|  5.14M|      if (check_output && size_t(end - utf8_output) < 3) {
  ------------------
  |  Branch (163:11): [Folded, False: 5.14M]
  |  Branch (163:27): [True: 0, False: 0]
  ------------------
  164|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  165|      0|                           utf8_output - start);
  166|      0|      }
  167|  5.14M|      *utf8_output++ = char((word >> 12) | 0b11100000);
  168|  5.14M|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  169|  5.14M|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  170|  5.14M|      pos++;
  171|  5.14M|    } else {
  172|       |
  173|  6.91k|      if (check_output && size_t(end - utf8_output) < 4) {
  ------------------
  |  Branch (173:11): [Folded, False: 6.91k]
  |  Branch (173:27): [True: 0, False: 0]
  ------------------
  174|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  175|      0|                           utf8_output - start);
  176|      0|      }
  177|       |      // must be a surrogate pair
  178|  6.91k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (178:11): [True: 72, False: 6.84k]
  ------------------
  179|     72|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  180|     72|      }
  181|  6.84k|      uint16_t diff = uint16_t(word - 0xD800);
  182|  6.84k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (182:11): [True: 158, False: 6.68k]
  ------------------
  183|    158|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  184|    158|      }
  185|  6.68k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (185:28): [Folded, False: 6.68k]
  ------------------
  186|  6.68k|                               ? u16_swap_bytes(data[pos + 1])
  187|  6.68k|                               : data[pos + 1];
  188|  6.68k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
  189|  6.68k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (189:11): [True: 244, False: 6.44k]
  ------------------
  190|    244|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  191|    244|      }
  192|  6.44k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
  193|       |      // will generate four UTF-8 bytes
  194|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
  195|  6.44k|      *utf8_output++ = char((value >> 18) | 0b11110000);
  196|  6.44k|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  197|  6.44k|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  198|  6.44k|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
  199|  6.44k|      pos += 2;
  200|  6.44k|    }
  201|  6.27M|  }
  202|  1.37k|  return full_result(error_code::SUCCESS, pos, utf8_output - start);
  203|  1.84k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf819convert_with_errorsILNS_10endiannessE1ELb0EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT1_Esr7simdutf6detailE26index_assignable_from_charIT2_EEENS_11full_resultES8_mS9_m:
  101|  1.38k|                                                    size_t utf8_len = 0) {
  102|  1.38k|  if (check_output && utf8_len == 0) {
  ------------------
  |  Branch (102:7): [Folded, False: 1.38k]
  |  Branch (102:23): [True: 0, False: 0]
  ------------------
  103|      0|    return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, 0, 0);
  104|      0|  }
  105|       |
  106|  1.38k|  size_t pos = 0;
  107|  1.38k|  auto start = utf8_output;
  108|  1.38k|  auto end = utf8_output + utf8_len;
  109|       |
  110|  2.18M|  while (pos < len) {
  ------------------
  |  Branch (110:10): [True: 2.18M, False: 958]
  ------------------
  111|       |#if SIMDUTF_CPLUSPLUS23
  112|       |    if !consteval
  113|       |#endif
  114|  2.18M|    {
  115|       |      // try to convert the next block of 8 bytes
  116|  2.18M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (116:11): [True: 2.18M, False: 2.20k]
  ------------------
  117|       |                            // they are ascii
  118|  2.18M|        uint64_t v;
  119|  2.18M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
  120|       |        if constexpr (!match_system(big_endian))
  121|  2.18M|          v = (v >> 8) | (v << (64 - 8));
  122|  2.18M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (122:13): [True: 1.38M, False: 799k]
  ------------------
  123|  1.38M|          size_t final_pos = pos + 4;
  124|  6.91M|          while (pos < final_pos) {
  ------------------
  |  Branch (124:18): [True: 5.53M, False: 1.38M]
  ------------------
  125|  5.53M|            if (check_output && size_t(end - utf8_output) < 1) {
  ------------------
  |  Branch (125:17): [Folded, False: 5.53M]
  |  Branch (125:33): [True: 0, False: 0]
  ------------------
  126|      0|              return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  127|      0|                                 utf8_output - start);
  128|      0|            }
  129|  5.53M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (129:30): [True: 5.53M, Folded]
  ------------------
  130|  5.53M|                                 ? char(u16_swap_bytes(data[pos]))
  131|  5.53M|                                 : char(data[pos]);
  132|  5.53M|            pos++;
  133|  5.53M|          }
  134|  1.38M|          continue;
  135|  1.38M|        }
  136|  2.18M|      }
  137|  2.18M|    }
  138|       |
  139|   801k|    uint16_t word =
  140|   801k|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (140:9): [True: 801k, Folded]
  ------------------
  141|   801k|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (141:9): [True: 267k, False: 534k]
  ------------------
  142|       |      // will generate one UTF-8 bytes
  143|   267k|      if (check_output && size_t(end - utf8_output) < 1) {
  ------------------
  |  Branch (143:11): [Folded, False: 267k]
  |  Branch (143:27): [True: 0, False: 0]
  ------------------
  144|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  145|      0|                           utf8_output - start);
  146|      0|      }
  147|   267k|      *utf8_output++ = char(word);
  148|   267k|      pos++;
  149|   534k|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (149:16): [True: 108k, False: 425k]
  ------------------
  150|       |      // will generate two UTF-8 bytes
  151|       |      // we have 0b110XXXXX 0b10XXXXXX
  152|   108k|      if (check_output && size_t(end - utf8_output) < 2) {
  ------------------
  |  Branch (152:11): [Folded, False: 108k]
  |  Branch (152:27): [True: 0, False: 0]
  ------------------
  153|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  154|      0|                           utf8_output - start);
  155|      0|      }
  156|   108k|      *utf8_output++ = char((word >> 6) | 0b11000000);
  157|   108k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  158|   108k|      pos++;
  159|       |
  160|   425k|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (160:16): [True: 417k, False: 8.52k]
  ------------------
  161|       |      // will generate three UTF-8 bytes
  162|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
  163|   417k|      if (check_output && size_t(end - utf8_output) < 3) {
  ------------------
  |  Branch (163:11): [Folded, False: 417k]
  |  Branch (163:27): [True: 0, False: 0]
  ------------------
  164|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  165|      0|                           utf8_output - start);
  166|      0|      }
  167|   417k|      *utf8_output++ = char((word >> 12) | 0b11100000);
  168|   417k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  169|   417k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  170|   417k|      pos++;
  171|   417k|    } else {
  172|       |
  173|  8.52k|      if (check_output && size_t(end - utf8_output) < 4) {
  ------------------
  |  Branch (173:11): [Folded, False: 8.52k]
  |  Branch (173:27): [True: 0, False: 0]
  ------------------
  174|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  175|      0|                           utf8_output - start);
  176|      0|      }
  177|       |      // must be a surrogate pair
  178|  8.52k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (178:11): [True: 54, False: 8.46k]
  ------------------
  179|     54|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  180|     54|      }
  181|  8.46k|      uint16_t diff = uint16_t(word - 0xD800);
  182|  8.46k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (182:11): [True: 132, False: 8.33k]
  ------------------
  183|    132|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  184|    132|      }
  185|  8.33k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (185:28): [True: 8.33k, Folded]
  ------------------
  186|  8.33k|                               ? u16_swap_bytes(data[pos + 1])
  187|  8.33k|                               : data[pos + 1];
  188|  8.33k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
  189|  8.33k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (189:11): [True: 242, False: 8.09k]
  ------------------
  190|    242|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  191|    242|      }
  192|  8.09k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
  193|       |      // will generate four UTF-8 bytes
  194|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
  195|  8.09k|      *utf8_output++ = char((value >> 18) | 0b11110000);
  196|  8.09k|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  197|  8.09k|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  198|  8.09k|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
  199|  8.09k|      pos += 2;
  200|  8.09k|    }
  201|   801k|  }
  202|    958|  return full_result(error_code::SUCCESS, pos, utf8_output - start);
  203|  1.38k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf813convert_validILNS_10endiannessE0EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT0_Esr7simdutf6detailE26index_assignable_from_charIT1_EEEmS8_mS9_:
   17|    402|                                         OutputPtr utf8_output) {
   18|    402|  size_t pos = 0;
   19|    402|  auto start = utf8_output;
   20|  2.68M|  while (pos < len) {
  ------------------
  |  Branch (20:10): [True: 2.68M, False: 402]
  ------------------
   21|       |#if SIMDUTF_CPLUSPLUS23
   22|       |    if !consteval
   23|       |#endif
   24|  2.68M|    {
   25|       |      // try to convert the next block of 4 ASCII characters
   26|  2.68M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (26:11): [True: 2.68M, False: 832]
  ------------------
   27|       |                            // they are ascii
   28|  2.68M|        uint64_t v;
   29|  2.68M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   30|       |        if constexpr (!match_system(big_endian)) {
   31|       |          v = (v >> 8) | (v << (64 - 8));
   32|       |        }
   33|  2.68M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (33:13): [True: 1.66M, False: 1.01M]
  ------------------
   34|  1.66M|          size_t final_pos = pos + 4;
   35|  8.34M|          while (pos < final_pos) {
  ------------------
  |  Branch (35:18): [True: 6.67M, False: 1.66M]
  ------------------
   36|  6.67M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (36:30): [Folded, False: 6.67M]
  ------------------
   37|  6.67M|                                 ? char(u16_swap_bytes(data[pos]))
   38|  6.67M|                                 : char(data[pos]);
   39|  6.67M|            pos++;
   40|  6.67M|          }
   41|  1.66M|          continue;
   42|  1.66M|        }
   43|  2.68M|      }
   44|  2.68M|    }
   45|       |
   46|  1.01M|    uint16_t word =
   47|  1.01M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (47:9): [Folded, False: 1.01M]
  ------------------
   48|  1.01M|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (48:9): [True: 196k, False: 822k]
  ------------------
   49|       |      // will generate one UTF-8 bytes
   50|   196k|      *utf8_output++ = char(word);
   51|   196k|      pos++;
   52|   822k|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (52:16): [True: 76.0k, False: 746k]
  ------------------
   53|       |      // will generate two UTF-8 bytes
   54|       |      // we have 0b110XXXXX 0b10XXXXXX
   55|  76.0k|      *utf8_output++ = char((word >> 6) | 0b11000000);
   56|  76.0k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   57|  76.0k|      pos++;
   58|   746k|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (58:16): [True: 740k, False: 5.64k]
  ------------------
   59|       |      // will generate three UTF-8 bytes
   60|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
   61|   740k|      *utf8_output++ = char((word >> 12) | 0b11100000);
   62|   740k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   63|   740k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   64|   740k|      pos++;
   65|   740k|    } else {
   66|       |      // must be a surrogate pair
   67|  5.64k|      uint16_t diff = uint16_t(word - 0xD800);
   68|  5.64k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (68:11): [True: 0, False: 5.64k]
  ------------------
   69|      0|        return 0;
   70|      0|      } // minimal bound checking
   71|  5.64k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (71:28): [Folded, False: 5.64k]
  ------------------
   72|  5.64k|                               ? u16_swap_bytes(data[pos + 1])
   73|  5.64k|                               : data[pos + 1];
   74|  5.64k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   75|  5.64k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   76|       |      // will generate four UTF-8 bytes
   77|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
   78|  5.64k|      *utf8_output++ = char((value >> 18) | 0b11110000);
   79|  5.64k|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
   80|  5.64k|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
   81|  5.64k|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
   82|  5.64k|      pos += 2;
   83|  5.64k|    }
   84|  1.01M|  }
   85|    402|  return utf8_output - start;
   86|    402|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf813convert_validILNS_10endiannessE1EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT0_Esr7simdutf6detailE26index_assignable_from_charIT1_EEEmS8_mS9_:
   17|    450|                                         OutputPtr utf8_output) {
   18|    450|  size_t pos = 0;
   19|    450|  auto start = utf8_output;
   20|  3.64M|  while (pos < len) {
  ------------------
  |  Branch (20:10): [True: 3.64M, False: 450]
  ------------------
   21|       |#if SIMDUTF_CPLUSPLUS23
   22|       |    if !consteval
   23|       |#endif
   24|  3.64M|    {
   25|       |      // try to convert the next block of 4 ASCII characters
   26|  3.64M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (26:11): [True: 3.64M, False: 934]
  ------------------
   27|       |                            // they are ascii
   28|  3.64M|        uint64_t v;
   29|  3.64M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   30|  3.64M|        if constexpr (!match_system(big_endian)) {
   31|  3.64M|          v = (v >> 8) | (v << (64 - 8));
   32|  3.64M|        }
   33|  3.64M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (33:13): [True: 2.38M, False: 1.26M]
  ------------------
   34|  2.38M|          size_t final_pos = pos + 4;
   35|  11.9M|          while (pos < final_pos) {
  ------------------
  |  Branch (35:18): [True: 9.52M, False: 2.38M]
  ------------------
   36|  9.52M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (36:30): [True: 9.52M, Folded]
  ------------------
   37|  9.52M|                                 ? char(u16_swap_bytes(data[pos]))
   38|  9.52M|                                 : char(data[pos]);
   39|  9.52M|            pos++;
   40|  9.52M|          }
   41|  2.38M|          continue;
   42|  2.38M|        }
   43|  3.64M|      }
   44|  3.64M|    }
   45|       |
   46|  1.26M|    uint16_t word =
   47|  1.26M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (47:9): [True: 1.26M, Folded]
  ------------------
   48|  1.26M|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (48:9): [True: 324k, False: 940k]
  ------------------
   49|       |      // will generate one UTF-8 bytes
   50|   324k|      *utf8_output++ = char(word);
   51|   324k|      pos++;
   52|   940k|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (52:16): [True: 146k, False: 793k]
  ------------------
   53|       |      // will generate two UTF-8 bytes
   54|       |      // we have 0b110XXXXX 0b10XXXXXX
   55|   146k|      *utf8_output++ = char((word >> 6) | 0b11000000);
   56|   146k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   57|   146k|      pos++;
   58|   793k|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (58:16): [True: 786k, False: 6.99k]
  ------------------
   59|       |      // will generate three UTF-8 bytes
   60|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
   61|   786k|      *utf8_output++ = char((word >> 12) | 0b11100000);
   62|   786k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   63|   786k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   64|   786k|      pos++;
   65|   786k|    } else {
   66|       |      // must be a surrogate pair
   67|  6.99k|      uint16_t diff = uint16_t(word - 0xD800);
   68|  6.99k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (68:11): [True: 0, False: 6.99k]
  ------------------
   69|      0|        return 0;
   70|      0|      } // minimal bound checking
   71|  6.99k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (71:28): [True: 6.99k, Folded]
  ------------------
   72|  6.99k|                               ? u16_swap_bytes(data[pos + 1])
   73|  6.99k|                               : data[pos + 1];
   74|  6.99k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   75|  6.99k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   76|       |      // will generate four UTF-8 bytes
   77|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
   78|  6.99k|      *utf8_output++ = char((value >> 18) | 0b11110000);
   79|  6.99k|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
   80|  6.99k|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
   81|  6.99k|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
   82|  6.99k|      pos += 2;
   83|  6.99k|    }
   84|  1.26M|  }
   85|    450|  return utf8_output - start;
   86|    450|}

_ZN7simdutf6scalar5utf328validateEPKDim:
   25|  5.74k|                                                        size_t len) noexcept {
   26|  5.74k|  return validate(reinterpret_cast<const uint32_t *>(buf), len);
   27|  5.74k|}
_ZN7simdutf6scalar5utf3220validate_with_errorsEPKDim:
   49|  7.89k|validate_with_errors(const char32_t *buf, size_t len) noexcept {
   50|  7.89k|  return validate_with_errors(reinterpret_cast<const uint32_t *>(buf), len);
   51|  7.89k|}
_ZN7simdutf6scalar5utf3222utf8_length_from_utf32EPKDim:
   54|  2.73k|                                                         size_t len) {
   55|       |  // We are not BOM aware.
   56|  2.73k|  size_t counter{0};
   57|  2.94M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (57:22): [True: 2.94M, False: 2.73k]
  ------------------
   58|       |    // credit: @ttsugriy  for the vectorizable approach
   59|  2.94M|    counter++;                                     // ASCII
   60|  2.94M|    counter += static_cast<size_t>(p[i] > 0x7F);   // two-byte
   61|  2.94M|    counter += static_cast<size_t>(p[i] > 0x7FF);  // three-byte
   62|  2.94M|    counter += static_cast<size_t>(p[i] > 0xFFFF); // four-bytes
   63|  2.94M|  }
   64|  2.73k|  return counter;
   65|  2.73k|}
_ZN7simdutf6scalar5utf3223utf16_length_from_utf32EPKDim:
   68|  4.29k|utf16_length_from_utf32(const char32_t *p, size_t len) {
   69|       |  // We are not BOM aware.
   70|  4.29k|  size_t counter{0};
   71|  3.86M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (71:22): [True: 3.85M, False: 4.29k]
  ------------------
   72|  3.85M|    counter++;                                     // non-surrogate word
   73|  3.85M|    counter += static_cast<size_t>(p[i] > 0xFFFF); // surrogate pair
   74|  3.85M|  }
   75|  4.29k|  return counter;
   76|  4.29k|}
_ZN7simdutf6scalar5utf328validateIPKjQsr7simdutf6detailE19indexes_into_uint32IT_EEEbS5_m:
   13|  5.74k|                                                      size_t len) noexcept {
   14|  5.74k|  uint64_t pos = 0;
   15|  1.17M|  for (; pos < len; pos++) {
  ------------------
  |  Branch (15:10): [True: 1.16M, False: 2.57k]
  ------------------
   16|  1.16M|    uint32_t word = data[pos];
   17|  1.16M|    if (word > 0x10FFFF || (word >= 0xD800 && word <= 0xDFFF)) {
  ------------------
  |  Branch (17:9): [True: 2.68k, False: 1.16M]
  |  Branch (17:29): [True: 39.0k, False: 1.12M]
  |  Branch (17:47): [True: 486, False: 38.5k]
  ------------------
   18|  3.17k|      return false;
   19|  3.17k|    }
   20|  1.16M|  }
   21|  2.57k|  return true;
   22|  5.74k|}
_ZN7simdutf6scalar5utf3220validate_with_errorsIPKjQsr7simdutf6detailE19indexes_into_uint32IT_EEENS_6resultES5_m:
   34|  7.89k|validate_with_errors(InputPtr data, size_t len) noexcept {
   35|  7.89k|  size_t pos = 0;
   36|  1.17M|  for (; pos < len; pos++) {
  ------------------
  |  Branch (36:10): [True: 1.17M, False: 2.57k]
  ------------------
   37|  1.17M|    uint32_t word = data[pos];
   38|  1.17M|    if (word > 0x10FFFF) {
  ------------------
  |  Branch (38:9): [True: 4.42k, False: 1.17M]
  ------------------
   39|  4.42k|      return result(error_code::TOO_LARGE, pos);
   40|  4.42k|    }
   41|  1.17M|    if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (41:9): [True: 40.4k, False: 1.13M]
  |  Branch (41:27): [True: 894, False: 39.5k]
  ------------------
   42|    894|      return result(error_code::SURROGATE, pos);
   43|    894|    }
   44|  1.17M|  }
   45|  2.57k|  return result(error_code::SUCCESS, pos);
   46|  7.89k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115utf32_to_latin17convertEPKDimPc:
   12|    450|                                          char *latin1_output) {
   13|    450|  char *start = latin1_output;
   14|    450|  uint32_t utf32_char;
   15|    450|  size_t pos = 0;
   16|    450|  uint32_t too_large = 0;
   17|       |
   18|  1.47M|  while (pos < len) {
  ------------------
  |  Branch (18:10): [True: 1.47M, False: 450]
  ------------------
   19|  1.47M|    utf32_char = (uint32_t)data[pos];
   20|  1.47M|    too_large |= utf32_char;
   21|  1.47M|    *latin1_output++ = (char)(utf32_char & 0xFF);
   22|  1.47M|    pos++;
   23|  1.47M|  }
   24|    450|  if ((too_large & 0xFFFFFF00) != 0) {
  ------------------
  |  Branch (24:7): [True: 368, False: 82]
  ------------------
   25|    368|    return 0;
   26|    368|  }
   27|     82|  return latin1_output - start;
   28|    450|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115utf32_to_latin119convert_with_errorsEPKDimPc:
   32|  1.22k|                                                      char *latin1_output) {
   33|  1.22k|  char *start{latin1_output};
   34|  1.22k|  size_t pos = 0;
   35|   150k|  while (pos < len) {
  ------------------
  |  Branch (35:10): [True: 150k, False: 92]
  ------------------
   36|       |#if SIMDUTF_CPLUSPLUS23
   37|       |    if !consteval
   38|       |#endif
   39|   150k|    {
   40|   150k|      if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (40:11): [True: 150k, False: 390]
  ------------------
   41|       |                            // they are Latin1
   42|   150k|        uint64_t v;
   43|   150k|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   44|   150k|        if ((v & 0xFFFFFF00FFFFFF00) == 0) {
  ------------------
  |  Branch (44:13): [True: 149k, False: 1.03k]
  ------------------
   45|   149k|          *latin1_output++ = char(data[pos]);
   46|   149k|          *latin1_output++ = char(data[pos + 1]);
   47|   149k|          pos += 2;
   48|   149k|          continue;
   49|   149k|        }
   50|   150k|      }
   51|   150k|    }
   52|       |
   53|  1.42k|    uint32_t utf32_char = data[pos];
   54|  1.42k|    if ((utf32_char & 0xFFFFFF00) ==
  ------------------
  |  Branch (54:9): [True: 292, False: 1.12k]
  ------------------
   55|  1.42k|        0) { // Check if the character can be represented in Latin-1
   56|    292|      *latin1_output++ = (char)(utf32_char & 0xFF);
   57|    292|      pos++;
   58|  1.12k|    } else {
   59|  1.12k|      return result(error_code::TOO_LARGE, pos);
   60|  1.12k|    };
   61|    292|  }
   62|     92|  return result(error_code::SUCCESS, latin1_output - start);
   63|  1.22k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf32_to_utf167convertILNS_10endiannessE0EEEmPKDimPDs:
   11|  1.84k|                                   char16_t *utf16_output) {
   12|  1.84k|  size_t pos = 0;
   13|  1.84k|  char16_t *start{utf16_output};
   14|   308k|  while (pos < len) {
  ------------------
  |  Branch (14:10): [True: 307k, False: 998]
  ------------------
   15|   307k|    uint32_t word = data[pos];
   16|   307k|    if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (16:9): [True: 295k, False: 12.0k]
  ------------------
   17|   295k|      if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (17:11): [True: 2.25k, False: 293k]
  |  Branch (17:29): [True: 188, False: 2.06k]
  ------------------
   18|    188|        return 0;
   19|    188|      }
   20|       |      // will not generate a surrogate pair
   21|   295k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (21:25): [Folded, False: 295k]
  ------------------
   22|   295k|                            ? char16_t(u16_swap_bytes(uint16_t(word)))
   23|   295k|                            : char16_t(word);
   24|   295k|    } else {
   25|       |      // will generate a surrogate pair
   26|  12.0k|      if (word > 0x10FFFF) {
  ------------------
  |  Branch (26:11): [True: 662, False: 11.3k]
  ------------------
   27|    662|        return 0;
   28|    662|      }
   29|  11.3k|      word -= 0x10000;
   30|  11.3k|      uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
   31|  11.3k|      uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
   32|       |      if constexpr (!match_system(big_endian)) {
   33|       |        high_surrogate = u16_swap_bytes(high_surrogate);
   34|       |        low_surrogate = u16_swap_bytes(low_surrogate);
   35|       |      }
   36|  11.3k|      *utf16_output++ = char16_t(high_surrogate);
   37|  11.3k|      *utf16_output++ = char16_t(low_surrogate);
   38|  11.3k|    }
   39|   306k|    pos++;
   40|   306k|  }
   41|    998|  return utf16_output - start;
   42|  1.84k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf32_to_utf167convertILNS_10endiannessE1EEEmPKDimPDs:
   11|  1.48k|                                   char16_t *utf16_output) {
   12|  1.48k|  size_t pos = 0;
   13|  1.48k|  char16_t *start{utf16_output};
   14|  45.5k|  while (pos < len) {
  ------------------
  |  Branch (14:10): [True: 44.8k, False: 702]
  ------------------
   15|  44.8k|    uint32_t word = data[pos];
   16|  44.8k|    if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (16:9): [True: 33.3k, False: 11.5k]
  ------------------
   17|  33.3k|      if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (17:11): [True: 1.11k, False: 32.2k]
  |  Branch (17:29): [True: 168, False: 950]
  ------------------
   18|    168|        return 0;
   19|    168|      }
   20|       |      // will not generate a surrogate pair
   21|  33.1k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (21:25): [True: 33.1k, Folded]
  ------------------
   22|  33.1k|                            ? char16_t(u16_swap_bytes(uint16_t(word)))
   23|  33.1k|                            : char16_t(word);
   24|  33.1k|    } else {
   25|       |      // will generate a surrogate pair
   26|  11.5k|      if (word > 0x10FFFF) {
  ------------------
  |  Branch (26:11): [True: 610, False: 10.9k]
  ------------------
   27|    610|        return 0;
   28|    610|      }
   29|  10.9k|      word -= 0x10000;
   30|  10.9k|      uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
   31|  10.9k|      uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
   32|  10.9k|      if constexpr (!match_system(big_endian)) {
   33|  10.9k|        high_surrogate = u16_swap_bytes(high_surrogate);
   34|  10.9k|        low_surrogate = u16_swap_bytes(low_surrogate);
   35|  10.9k|      }
   36|  10.9k|      *utf16_output++ = char16_t(high_surrogate);
   37|  10.9k|      *utf16_output++ = char16_t(low_surrogate);
   38|  10.9k|    }
   39|  44.0k|    pos++;
   40|  44.0k|  }
   41|    702|  return utf16_output - start;
   42|  1.48k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf32_to_utf1619convert_with_errorsILNS_10endiannessE0EEENS_6resultEPKDimPDs:
   46|  2.21k|                                               char16_t *utf16_output) {
   47|  2.21k|  size_t pos = 0;
   48|  2.21k|  char16_t *start{utf16_output};
   49|  64.3k|  while (pos < len) {
  ------------------
  |  Branch (49:10): [True: 63.8k, False: 514]
  ------------------
   50|  63.8k|    uint32_t word = data[pos];
   51|  63.8k|    if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (51:9): [True: 56.3k, False: 7.41k]
  ------------------
   52|  56.3k|      if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (52:11): [True: 1.83k, False: 54.5k]
  |  Branch (52:29): [True: 372, False: 1.46k]
  ------------------
   53|    372|        return result(error_code::SURROGATE, pos);
   54|    372|      }
   55|       |      // will not generate a surrogate pair
   56|  56.0k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (56:25): [Folded, False: 56.0k]
  ------------------
   57|  56.0k|                            ? char16_t(u16_swap_bytes(uint16_t(word)))
   58|  56.0k|                            : char16_t(word);
   59|  56.0k|    } else {
   60|       |      // will generate a surrogate pair
   61|  7.41k|      if (word > 0x10FFFF) {
  ------------------
  |  Branch (61:11): [True: 1.33k, False: 6.08k]
  ------------------
   62|  1.33k|        return result(error_code::TOO_LARGE, pos);
   63|  1.33k|      }
   64|  6.08k|      word -= 0x10000;
   65|  6.08k|      uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
   66|  6.08k|      uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
   67|       |      if constexpr (!match_system(big_endian)) {
   68|       |        high_surrogate = u16_swap_bytes(high_surrogate);
   69|       |        low_surrogate = u16_swap_bytes(low_surrogate);
   70|       |      }
   71|  6.08k|      *utf16_output++ = char16_t(high_surrogate);
   72|  6.08k|      *utf16_output++ = char16_t(low_surrogate);
   73|  6.08k|    }
   74|  62.1k|    pos++;
   75|  62.1k|  }
   76|    514|  return result(error_code::SUCCESS, utf16_output - start);
   77|  2.21k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf32_to_utf1619convert_with_errorsILNS_10endiannessE1EEENS_6resultEPKDimPDs:
   46|  1.98k|                                               char16_t *utf16_output) {
   47|  1.98k|  size_t pos = 0;
   48|  1.98k|  char16_t *start{utf16_output};
   49|  45.9k|  while (pos < len) {
  ------------------
  |  Branch (49:10): [True: 45.4k, False: 470]
  ------------------
   50|  45.4k|    uint32_t word = data[pos];
   51|  45.4k|    if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (51:9): [True: 41.1k, False: 4.30k]
  ------------------
   52|  41.1k|      if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (52:11): [True: 1.46k, False: 39.6k]
  |  Branch (52:29): [True: 234, False: 1.22k]
  ------------------
   53|    234|        return result(error_code::SURROGATE, pos);
   54|    234|      }
   55|       |      // will not generate a surrogate pair
   56|  40.8k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (56:25): [True: 40.8k, Folded]
  ------------------
   57|  40.8k|                            ? char16_t(u16_swap_bytes(uint16_t(word)))
   58|  40.8k|                            : char16_t(word);
   59|  40.8k|    } else {
   60|       |      // will generate a surrogate pair
   61|  4.30k|      if (word > 0x10FFFF) {
  ------------------
  |  Branch (61:11): [True: 1.27k, False: 3.02k]
  ------------------
   62|  1.27k|        return result(error_code::TOO_LARGE, pos);
   63|  1.27k|      }
   64|  3.02k|      word -= 0x10000;
   65|  3.02k|      uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
   66|  3.02k|      uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
   67|  3.02k|      if constexpr (!match_system(big_endian)) {
   68|  3.02k|        high_surrogate = u16_swap_bytes(high_surrogate);
   69|  3.02k|        low_surrogate = u16_swap_bytes(low_surrogate);
   70|  3.02k|      }
   71|  3.02k|      *utf16_output++ = char16_t(high_surrogate);
   72|  3.02k|      *utf16_output++ = char16_t(low_surrogate);
   73|  3.02k|    }
   74|  43.9k|    pos++;
   75|  43.9k|  }
   76|    470|  return result(error_code::SUCCESS, utf16_output - start);
   77|  1.98k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf32_to_utf1613convert_validILNS_10endiannessE0EEEmPKDimPDs:
   11|    320|                                         char16_t *utf16_output) {
   12|    320|  size_t pos = 0;
   13|    320|  char16_t *start{utf16_output};
   14|   659k|  while (pos < len) {
  ------------------
  |  Branch (14:10): [True: 659k, False: 320]
  ------------------
   15|   659k|    uint32_t word = data[pos];
   16|   659k|    if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (16:9): [True: 654k, False: 4.13k]
  ------------------
   17|       |      // will not generate a surrogate pair
   18|   654k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (18:25): [Folded, False: 654k]
  ------------------
   19|   654k|                            ? char16_t(u16_swap_bytes(uint16_t(word)))
   20|   654k|                            : char16_t(word);
   21|   654k|      pos++;
   22|   654k|    } else {
   23|       |      // will generate a surrogate pair
   24|  4.13k|      word -= 0x10000;
   25|  4.13k|      uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
   26|  4.13k|      uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
   27|       |      if constexpr (!match_system(big_endian)) {
   28|       |        high_surrogate = u16_swap_bytes(high_surrogate);
   29|       |        low_surrogate = u16_swap_bytes(low_surrogate);
   30|       |      }
   31|  4.13k|      *utf16_output++ = char16_t(high_surrogate);
   32|  4.13k|      *utf16_output++ = char16_t(low_surrogate);
   33|  4.13k|      pos++;
   34|  4.13k|    }
   35|   659k|  }
   36|    320|  return utf16_output - start;
   37|    320|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf32_to_utf1613convert_validILNS_10endiannessE1EEEmPKDimPDs:
   11|    132|                                         char16_t *utf16_output) {
   12|    132|  size_t pos = 0;
   13|    132|  char16_t *start{utf16_output};
   14|  15.2k|  while (pos < len) {
  ------------------
  |  Branch (14:10): [True: 15.1k, False: 132]
  ------------------
   15|  15.1k|    uint32_t word = data[pos];
   16|  15.1k|    if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (16:9): [True: 6.21k, False: 8.91k]
  ------------------
   17|       |      // will not generate a surrogate pair
   18|  6.21k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (18:25): [True: 6.21k, Folded]
  ------------------
   19|  6.21k|                            ? char16_t(u16_swap_bytes(uint16_t(word)))
   20|  6.21k|                            : char16_t(word);
   21|  6.21k|      pos++;
   22|  8.91k|    } else {
   23|       |      // will generate a surrogate pair
   24|  8.91k|      word -= 0x10000;
   25|  8.91k|      uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
   26|  8.91k|      uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
   27|  8.91k|      if constexpr (!match_system(big_endian)) {
   28|  8.91k|        high_surrogate = u16_swap_bytes(high_surrogate);
   29|  8.91k|        low_surrogate = u16_swap_bytes(low_surrogate);
   30|  8.91k|      }
   31|  8.91k|      *utf16_output++ = char16_t(high_surrogate);
   32|  8.91k|      *utf16_output++ = char16_t(low_surrogate);
   33|  8.91k|      pos++;
   34|  8.91k|    }
   35|  15.1k|  }
   36|    132|  return utf16_output - start;
   37|    132|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf32_to_utf87convertIPKDiPcQaasr7simdutf6detailE18indexes_into_utf32IT_Esr7simdutf6detailE26index_assignable_from_charIT0_EEEmS7_mS8_:
   17|  1.97k|                                   OutputPtr utf8_output) {
   18|  1.97k|  size_t pos = 0;
   19|  1.97k|  auto start = utf8_output;
   20|   362k|  while (pos < len) {
  ------------------
  |  Branch (20:10): [True: 361k, False: 830]
  ------------------
   21|       |#if SIMDUTF_CPLUSPLUS23
   22|       |    if !consteval
   23|       |#endif
   24|   361k|    { // try to convert the next block of 2 ASCII characters
   25|   361k|      if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (25:11): [True: 360k, False: 1.08k]
  ------------------
   26|       |                            // they are ascii
   27|   360k|        uint64_t v;
   28|   360k|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   29|   360k|        if ((v & 0xFFFFFF80FFFFFF80) == 0) {
  ------------------
  |  Branch (29:13): [True: 298k, False: 62.5k]
  ------------------
   30|   298k|          *utf8_output++ = char(data[pos]);
   31|   298k|          *utf8_output++ = char(data[pos + 1]);
   32|   298k|          pos += 2;
   33|   298k|          continue;
   34|   298k|        }
   35|   360k|      }
   36|   361k|    }
   37|       |
   38|  63.6k|    uint32_t word = data[pos];
   39|  63.6k|    if ((word & 0xFFFFFF80) == 0) {
  ------------------
  |  Branch (39:9): [True: 8.49k, False: 55.1k]
  ------------------
   40|       |      // will generate one UTF-8 bytes
   41|  8.49k|      *utf8_output++ = char(word);
   42|  8.49k|      pos++;
   43|  55.1k|    } else if ((word & 0xFFFFF800) == 0) {
  ------------------
  |  Branch (43:16): [True: 36.0k, False: 19.0k]
  ------------------
   44|       |      // will generate two UTF-8 bytes
   45|       |      // we have 0b110XXXXX 0b10XXXXXX
   46|  36.0k|      *utf8_output++ = char((word >> 6) | 0b11000000);
   47|  36.0k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   48|  36.0k|      pos++;
   49|  36.0k|    } else if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (49:16): [True: 13.1k, False: 5.87k]
  ------------------
   50|       |      // will generate three UTF-8 bytes
   51|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
   52|  13.1k|      if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (52:11): [True: 9.23k, False: 3.92k]
  |  Branch (52:29): [True: 188, False: 9.04k]
  ------------------
   53|    188|        return 0;
   54|    188|      }
   55|  12.9k|      *utf8_output++ = char((word >> 12) | 0b11100000);
   56|  12.9k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   57|  12.9k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   58|  12.9k|      pos++;
   59|  12.9k|    } else {
   60|       |      // will generate four UTF-8 bytes
   61|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
   62|  5.87k|      if (word > 0x10FFFF) {
  ------------------
  |  Branch (62:11): [True: 956, False: 4.92k]
  ------------------
   63|    956|        return 0;
   64|    956|      }
   65|  4.92k|      *utf8_output++ = char((word >> 18) | 0b11110000);
   66|  4.92k|      *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
   67|  4.92k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   68|  4.92k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   69|  4.92k|      pos++;
   70|  4.92k|    }
   71|  63.6k|  }
   72|    830|  return utf8_output - start;
   73|  1.97k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf32_to_utf819convert_with_errorsIPKDiPcQaasr7simdutf6detailE18indexes_into_utf32IT_Esr7simdutf6detailE26index_assignable_from_charIT0_EEENS_6resultES7_mS8_:
   81|  2.58k|                                               OutputPtr utf8_output) {
   82|  2.58k|  size_t pos = 0;
   83|  2.58k|  auto start = utf8_output;
   84|   136k|  while (pos < len) {
  ------------------
  |  Branch (84:10): [True: 136k, False: 616]
  ------------------
   85|       |#if SIMDUTF_CPLUSPLUS23
   86|       |    if !consteval
   87|       |#endif
   88|   136k|    { // try to convert the next block of 2 ASCII characters
   89|   136k|      if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (89:11): [True: 135k, False: 1.02k]
  ------------------
   90|       |                            // they are ascii
   91|   135k|        uint64_t v;
   92|   135k|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   93|   135k|        if ((v & 0xFFFFFF80FFFFFF80) == 0) {
  ------------------
  |  Branch (93:13): [True: 75.6k, False: 59.6k]
  ------------------
   94|  75.6k|          *utf8_output++ = char(data[pos]);
   95|  75.6k|          *utf8_output++ = char(data[pos + 1]);
   96|  75.6k|          pos += 2;
   97|  75.6k|          continue;
   98|  75.6k|        }
   99|   135k|      }
  100|   136k|    }
  101|       |
  102|  60.6k|    uint32_t word = data[pos];
  103|  60.6k|    if ((word & 0xFFFFFF80) == 0) {
  ------------------
  |  Branch (103:9): [True: 6.77k, False: 53.9k]
  ------------------
  104|       |      // will generate one UTF-8 bytes
  105|  6.77k|      *utf8_output++ = char(word);
  106|  6.77k|      pos++;
  107|  53.9k|    } else if ((word & 0xFFFFF800) == 0) {
  ------------------
  |  Branch (107:16): [True: 33.3k, False: 20.5k]
  ------------------
  108|       |      // will generate two UTF-8 bytes
  109|       |      // we have 0b110XXXXX 0b10XXXXXX
  110|  33.3k|      *utf8_output++ = char((word >> 6) | 0b11000000);
  111|  33.3k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  112|  33.3k|      pos++;
  113|  33.3k|    } else if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (113:16): [True: 10.0k, False: 10.5k]
  ------------------
  114|       |      // will generate three UTF-8 bytes
  115|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
  116|  10.0k|      if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (116:11): [True: 6.16k, False: 3.91k]
  |  Branch (116:29): [True: 324, False: 5.83k]
  ------------------
  117|    324|        return result(error_code::SURROGATE, pos);
  118|    324|      }
  119|  9.74k|      *utf8_output++ = char((word >> 12) | 0b11100000);
  120|  9.74k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  121|  9.74k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  122|  9.74k|      pos++;
  123|  10.5k|    } else {
  124|       |      // will generate four UTF-8 bytes
  125|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
  126|  10.5k|      if (word > 0x10FFFF) {
  ------------------
  |  Branch (126:11): [True: 1.64k, False: 8.87k]
  ------------------
  127|  1.64k|        return result(error_code::TOO_LARGE, pos);
  128|  1.64k|      }
  129|  8.87k|      *utf8_output++ = char((word >> 18) | 0b11110000);
  130|  8.87k|      *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
  131|  8.87k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  132|  8.87k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  133|  8.87k|      pos++;
  134|  8.87k|    }
  135|  60.6k|  }
  136|    616|  return result(error_code::SUCCESS, utf8_output - start);
  137|  2.58k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf32_to_utf813convert_validIPKDiPcQaasr7simdutf6detailE18indexes_into_utf32IT_Esr7simdutf6detailE26index_assignable_from_charIT0_EEEmS7_mS8_:
   17|    216|                                         OutputPtr utf8_output) {
   18|    216|  size_t pos = 0;
   19|    216|  auto start = utf8_output;
   20|  12.6k|  while (pos < len) {
  ------------------
  |  Branch (20:10): [True: 12.4k, False: 216]
  ------------------
   21|       |#if SIMDUTF_CPLUSPLUS23
   22|       |    if !consteval
   23|       |#endif
   24|  12.4k|    { // try to convert the next block of 2 ASCII characters
   25|  12.4k|      if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (25:11): [True: 12.2k, False: 192]
  ------------------
   26|       |                            // they are ascii
   27|  12.2k|        uint64_t v;
   28|  12.2k|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   29|  12.2k|        if ((v & 0xFFFFFF80FFFFFF80) == 0) {
  ------------------
  |  Branch (29:13): [True: 1.74k, False: 10.5k]
  ------------------
   30|  1.74k|          *utf8_output++ = char(data[pos]);
   31|  1.74k|          *utf8_output++ = char(data[pos + 1]);
   32|  1.74k|          pos += 2;
   33|  1.74k|          continue;
   34|  1.74k|        }
   35|  12.2k|      }
   36|  12.4k|    }
   37|       |
   38|  10.7k|    uint32_t word = data[pos];
   39|  10.7k|    if ((word & 0xFFFFFF80) == 0) {
  ------------------
  |  Branch (39:9): [True: 888, False: 9.84k]
  ------------------
   40|       |      // will generate one UTF-8 bytes
   41|    888|      *utf8_output++ = char(word);
   42|    888|      pos++;
   43|  9.84k|    } else if ((word & 0xFFFFF800) == 0) {
  ------------------
  |  Branch (43:16): [True: 6.05k, False: 3.79k]
  ------------------
   44|       |      // will generate two UTF-8 bytes
   45|       |      // we have 0b110XXXXX 0b10XXXXXX
   46|  6.05k|      *utf8_output++ = char((word >> 6) | 0b11000000);
   47|  6.05k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   48|  6.05k|      pos++;
   49|  6.05k|    } else if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (49:16): [True: 1.71k, False: 2.08k]
  ------------------
   50|       |      // will generate three UTF-8 bytes
   51|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
   52|  1.71k|      *utf8_output++ = char((word >> 12) | 0b11100000);
   53|  1.71k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   54|  1.71k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   55|  1.71k|      pos++;
   56|  2.08k|    } else {
   57|       |      // will generate four UTF-8 bytes
   58|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
   59|  2.08k|      *utf8_output++ = char((word >> 18) | 0b11110000);
   60|  2.08k|      *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
   61|  2.08k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   62|  2.08k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   63|  2.08k|      pos++;
   64|  2.08k|    }
   65|  10.7k|  }
   66|    216|  return utf8_output - start;
   67|    216|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf88validateEPKcm:
  113|  4.20k|                                                        size_t len) noexcept {
  114|  4.20k|  return validate(reinterpret_cast<const uint8_t *>(buf), len);
  115|  4.20k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf88validateIPKhEEbT_m:
   14|  4.20k|                                                      size_t len) noexcept {
   15|  4.20k|  static_assert(
   16|  4.20k|      std::is_same<typename std::decay<decltype(*data)>::type, uint8_t>::value,
   17|  4.20k|      "dereferencing the data pointer must result in a uint8_t");
   18|  4.20k|  uint64_t pos = 0;
   19|  4.20k|  uint32_t code_point = 0;
   20|  9.83M|  while (pos < len) {
  ------------------
  |  Branch (20:10): [True: 9.83M, False: 638]
  ------------------
   21|  9.83M|    uint64_t next_pos;
   22|       |#if SIMDUTF_CPLUSPLUS23
   23|       |    if !consteval
   24|       |#endif
   25|  9.83M|    { // check if the next 16 bytes are ascii.
   26|  9.83M|      next_pos = pos + 16;
   27|  9.83M|      if (next_pos <= len) { // if it is safe to read 16 more bytes, check
  ------------------
  |  Branch (27:11): [True: 9.82M, False: 3.58k]
  ------------------
   28|       |                             // that they are ascii
   29|  9.82M|        uint64_t v1{};
   30|  9.82M|        std::memcpy(&v1, data + pos, sizeof(uint64_t));
   31|  9.82M|        uint64_t v2{};
   32|  9.82M|        std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   33|  9.82M|        uint64_t v{v1 | v2};
   34|  9.82M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (34:13): [True: 8.45M, False: 1.37M]
  ------------------
   35|  8.45M|          pos = next_pos;
   36|  8.45M|          continue;
   37|  8.45M|        }
   38|  9.82M|      }
   39|  9.83M|    }
   40|       |
   41|  1.37M|    unsigned char byte = data[pos];
   42|       |
   43|  6.36M|    while (byte < 0b10000000) {
  ------------------
  |  Branch (43:12): [True: 4.98M, False: 1.37M]
  ------------------
   44|  4.98M|      if (++pos == len) {
  ------------------
  |  Branch (44:11): [True: 1.14k, False: 4.98M]
  ------------------
   45|  1.14k|        return true;
   46|  1.14k|      }
   47|  4.98M|      byte = data[pos];
   48|  4.98M|    }
   49|       |
   50|  1.37M|    if ((byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (50:9): [True: 834k, False: 539k]
  ------------------
   51|   834k|      next_pos = pos + 2;
   52|   834k|      if (next_pos > len) {
  ------------------
  |  Branch (52:11): [True: 89, False: 834k]
  ------------------
   53|     89|        return false;
   54|     89|      }
   55|   834k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (55:11): [True: 364, False: 834k]
  ------------------
   56|    364|        return false;
   57|    364|      }
   58|       |      // range check
   59|   834k|      code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
   60|   834k|      if (code_point < 0x80) {
  ------------------
  |  Branch (60:11): [True: 44, False: 833k]
  ------------------
   61|     44|        return false;
   62|     44|      }
   63|   834k|    } else if ((byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (63:16): [True: 467k, False: 72.3k]
  ------------------
   64|   467k|      next_pos = pos + 3;
   65|   467k|      if (next_pos > len) {
  ------------------
  |  Branch (65:11): [True: 48, False: 467k]
  ------------------
   66|     48|        return false;
   67|     48|      }
   68|   467k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (68:11): [True: 169, False: 466k]
  ------------------
   69|    169|        return false;
   70|    169|      }
   71|   466k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (71:11): [True: 69, False: 466k]
  ------------------
   72|     69|        return false;
   73|     69|      }
   74|       |      // range check
   75|   466k|      code_point = (byte & 0b00001111) << 12 |
   76|   466k|                   (data[pos + 1] & 0b00111111) << 6 |
   77|   466k|                   (data[pos + 2] & 0b00111111);
   78|   466k|      if ((code_point < 0x800) ||
  ------------------
  |  Branch (78:11): [True: 46, False: 466k]
  ------------------
   79|   466k|          (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (79:12): [True: 116k, False: 350k]
  |  Branch (79:35): [True: 19, False: 116k]
  ------------------
   80|     65|        return false;
   81|     65|      }
   82|   466k|    } else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (82:16): [True: 71.1k, False: 1.21k]
  ------------------
   83|  71.1k|      next_pos = pos + 4;
   84|  71.1k|      if (next_pos > len) {
  ------------------
  |  Branch (84:11): [True: 41, False: 71.1k]
  ------------------
   85|     41|        return false;
   86|     41|      }
   87|  71.1k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (87:11): [True: 133, False: 71.0k]
  ------------------
   88|    133|        return false;
   89|    133|      }
   90|  71.0k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (90:11): [True: 57, False: 70.9k]
  ------------------
   91|     57|        return false;
   92|     57|      }
   93|  70.9k|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (93:11): [True: 36, False: 70.9k]
  ------------------
   94|     36|        return false;
   95|     36|      }
   96|       |      // range check
   97|  70.9k|      code_point =
   98|  70.9k|          (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
   99|  70.9k|          (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
  100|  70.9k|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (100:11): [True: 36, False: 70.8k]
  |  Branch (100:35): [True: 58, False: 70.8k]
  ------------------
  101|     94|        return false;
  102|     94|      }
  103|  70.9k|    } else {
  104|       |      // we may have a continuation
  105|  1.21k|      return false;
  106|  1.21k|    }
  107|  1.37M|    pos = next_pos;
  108|  1.37M|  }
  109|    638|  return true;
  110|  4.20k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf820validate_with_errorsEPKcm:
  221|  8.77k|validate_with_errors(const char *buf, size_t len) noexcept {
  222|  8.77k|  return validate_with_errors(reinterpret_cast<const uint8_t *>(buf), len);
  223|  8.77k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf820validate_with_errorsIPKhEENS_6resultET_m:
  119|  8.77k|validate_with_errors(BytePtr data, size_t len) noexcept {
  120|  8.77k|  static_assert(
  121|  8.77k|      std::is_same<typename std::decay<decltype(*data)>::type, uint8_t>::value,
  122|  8.77k|      "dereferencing the data pointer must result in a uint8_t");
  123|  8.77k|  size_t pos = 0;
  124|  8.77k|  uint32_t code_point = 0;
  125|  9.84M|  while (pos < len) {
  ------------------
  |  Branch (125:10): [True: 9.84M, False: 638]
  ------------------
  126|       |    // check of the next 16 bytes are ascii.
  127|  9.84M|    size_t next_pos = pos + 16;
  128|  9.84M|    if (next_pos <=
  ------------------
  |  Branch (128:9): [True: 9.83M, False: 5.75k]
  ------------------
  129|  9.84M|        len) { // if it is safe to read 16 more bytes, check that they are ascii
  130|  9.83M|      uint64_t v1;
  131|  9.83M|      std::memcpy(&v1, data + pos, sizeof(uint64_t));
  132|  9.83M|      uint64_t v2;
  133|  9.83M|      std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  134|  9.83M|      uint64_t v{v1 | v2};
  135|  9.83M|      if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (135:11): [True: 8.45M, False: 1.37M]
  ------------------
  136|  8.45M|        pos = next_pos;
  137|  8.45M|        continue;
  138|  8.45M|      }
  139|  9.83M|    }
  140|  1.38M|    unsigned char byte = data[pos];
  141|       |
  142|  6.39M|    while (byte < 0b10000000) {
  ------------------
  |  Branch (142:12): [True: 5.01M, False: 1.38M]
  ------------------
  143|  5.01M|      if (++pos == len) {
  ------------------
  |  Branch (143:11): [True: 1.14k, False: 5.00M]
  ------------------
  144|  1.14k|        return result(error_code::SUCCESS, len);
  145|  1.14k|      }
  146|  5.00M|      byte = data[pos];
  147|  5.00M|    }
  148|       |
  149|  1.38M|    if ((byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (149:9): [True: 838k, False: 544k]
  ------------------
  150|   838k|      next_pos = pos + 2;
  151|   838k|      if (next_pos > len) {
  ------------------
  |  Branch (151:11): [True: 267, False: 838k]
  ------------------
  152|    267|        return result(error_code::TOO_SHORT, pos);
  153|    267|      }
  154|   838k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (154:11): [True: 1.09k, False: 836k]
  ------------------
  155|  1.09k|        return result(error_code::TOO_SHORT, pos);
  156|  1.09k|      }
  157|       |      // range check
  158|   836k|      code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
  159|   836k|      if (code_point < 0x80) {
  ------------------
  |  Branch (159:11): [True: 132, False: 836k]
  ------------------
  160|    132|        return result(error_code::OVERLONG, pos);
  161|    132|      }
  162|   836k|    } else if ((byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (162:16): [True: 469k, False: 75.7k]
  ------------------
  163|   469k|      next_pos = pos + 3;
  164|   469k|      if (next_pos > len) {
  ------------------
  |  Branch (164:11): [True: 144, False: 468k]
  ------------------
  165|    144|        return result(error_code::TOO_SHORT, pos);
  166|    144|      }
  167|   468k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (167:11): [True: 507, False: 468k]
  ------------------
  168|    507|        return result(error_code::TOO_SHORT, pos);
  169|    507|      }
  170|   468k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (170:11): [True: 207, False: 468k]
  ------------------
  171|    207|        return result(error_code::TOO_SHORT, pos);
  172|    207|      }
  173|       |      // range check
  174|   468k|      code_point = (byte & 0b00001111) << 12 |
  175|   468k|                   (data[pos + 1] & 0b00111111) << 6 |
  176|   468k|                   (data[pos + 2] & 0b00111111);
  177|   468k|      if (code_point < 0x800) {
  ------------------
  |  Branch (177:11): [True: 138, False: 468k]
  ------------------
  178|    138|        return result(error_code::OVERLONG, pos);
  179|    138|      }
  180|   468k|      if (0xd7ff < code_point && code_point < 0xe000) {
  ------------------
  |  Branch (180:11): [True: 116k, False: 351k]
  |  Branch (180:34): [True: 57, False: 116k]
  ------------------
  181|     57|        return result(error_code::SURROGATE, pos);
  182|     57|      }
  183|   468k|    } else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (183:16): [True: 72.3k, False: 3.36k]
  ------------------
  184|  72.3k|      next_pos = pos + 4;
  185|  72.3k|      if (next_pos > len) {
  ------------------
  |  Branch (185:11): [True: 123, False: 72.2k]
  ------------------
  186|    123|        return result(error_code::TOO_SHORT, pos);
  187|    123|      }
  188|  72.2k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (188:11): [True: 399, False: 71.8k]
  ------------------
  189|    399|        return result(error_code::TOO_SHORT, pos);
  190|    399|      }
  191|  71.8k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (191:11): [True: 171, False: 71.6k]
  ------------------
  192|    171|        return result(error_code::TOO_SHORT, pos);
  193|    171|      }
  194|  71.6k|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (194:11): [True: 108, False: 71.5k]
  ------------------
  195|    108|        return result(error_code::TOO_SHORT, pos);
  196|    108|      }
  197|       |      // range check
  198|  71.5k|      code_point =
  199|  71.5k|          (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
  200|  71.5k|          (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
  201|  71.5k|      if (code_point <= 0xffff) {
  ------------------
  |  Branch (201:11): [True: 108, False: 71.4k]
  ------------------
  202|    108|        return result(error_code::OVERLONG, pos);
  203|    108|      }
  204|  71.4k|      if (0x10ffff < code_point) {
  ------------------
  |  Branch (204:11): [True: 174, False: 71.2k]
  ------------------
  205|    174|        return result(error_code::TOO_LARGE, pos);
  206|    174|      }
  207|  71.4k|    } else {
  208|       |      // we either have too many continuation bytes or an invalid leading byte
  209|  3.36k|      if ((byte & 0b11000000) == 0b10000000) {
  ------------------
  |  Branch (209:11): [True: 1.74k, False: 1.62k]
  ------------------
  210|  1.74k|        return result(error_code::TOO_LONG, pos);
  211|  1.74k|      } else {
  212|  1.62k|        return result(error_code::HEADER_BITS, pos);
  213|  1.62k|      }
  214|  3.36k|    }
  215|  1.37M|    pos = next_pos;
  216|  1.37M|  }
  217|    638|  return result(error_code::SUCCESS, len);
  218|  8.77k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf831rewind_and_validate_with_errorsEPKcS4_m:
  232|  4.85k|    const char *start, const char *buf, size_t len) noexcept {
  233|       |  // First check that we start with a leading byte
  234|  4.85k|  if ((*start & 0b11000000) == 0b10000000) {
  ------------------
  |  Branch (234:7): [True: 294, False: 4.56k]
  ------------------
  235|    294|    return result(error_code::TOO_LONG, 0);
  236|    294|  }
  237|  4.56k|  size_t extra_len{0};
  238|       |  // A leading byte cannot be further than 4 bytes away
  239|  4.82k|  for (int i = 0; i < 5; i++) {
  ------------------
  |  Branch (239:19): [True: 4.82k, False: 0]
  ------------------
  240|  4.82k|    unsigned char byte = *buf;
  241|  4.82k|    if ((byte & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (241:9): [True: 4.56k, False: 262]
  ------------------
  242|  4.56k|      break;
  243|  4.56k|    } else {
  244|    262|      buf--;
  245|    262|      extra_len++;
  246|    262|    }
  247|  4.82k|  }
  248|       |
  249|  4.56k|  result res = validate_with_errors(buf, len + extra_len);
  250|  4.56k|  res.count -= extra_len; // Might underflow
  251|  4.56k|  return res;
  252|  4.85k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf817count_code_pointsIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEEmS6_m:
  454|  18.3k|simdutf_constexpr23 size_t count_code_points(InputPtr data, size_t len) {
  455|  18.3k|  size_t counter{0};
  456|   307M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (456:22): [True: 307M, False: 18.3k]
  ------------------
  457|       |    // -65 is 0b10111111, anything larger in two-complement's should start a new
  458|       |    // code point.
  459|   307M|    if (int8_t(data[i]) > -65) {
  ------------------
  |  Branch (459:9): [True: 301M, False: 5.38M]
  ------------------
  460|   301M|      counter++;
  461|   301M|    }
  462|   307M|  }
  463|  18.3k|  return counter;
  464|  18.3k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf822utf16_length_from_utf8IPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEEmS6_m:
  470|  6.86k|simdutf_constexpr23 size_t utf16_length_from_utf8(InputPtr data, size_t len) {
  471|  6.86k|  size_t counter{0};
  472|   115M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (472:22): [True: 115M, False: 6.86k]
  ------------------
  473|   115M|    if (int8_t(data[i]) > -65) {
  ------------------
  |  Branch (473:9): [True: 113M, False: 2.01M]
  ------------------
  474|   113M|      counter++;
  475|   113M|    }
  476|   115M|    if (uint8_t(data[i]) >= 240) {
  ------------------
  |  Branch (476:9): [True: 172k, False: 115M]
  ------------------
  477|   172k|      counter++;
  478|   172k|    }
  479|   115M|  }
  480|  6.86k|  return counter;
  481|  6.86k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf8_to_latin130rewind_and_convert_with_errorsEmPKcmPc:
  176|    928|                                             char *latin1_output) {
  177|    928|  size_t extra_len{0};
  178|       |  // We potentially need to go back in time and find a leading byte.
  179|       |  // In theory '3' would be sufficient, but sometimes the error can go back
  180|       |  // quite far.
  181|    928|  size_t how_far_back = prior_bytes;
  182|       |  // size_t how_far_back = 3; // 3 bytes in the past + current position
  183|       |  // if(how_far_back >= prior_bytes) { how_far_back = prior_bytes; }
  184|    928|  bool found_leading_bytes{false};
  185|       |  // important: it is i <= how_far_back and not 'i < how_far_back'.
  186|  1.05k|  for (size_t i = 0; i <= how_far_back; i++) {
  ------------------
  |  Branch (186:22): [True: 992, False: 60]
  ------------------
  187|    992|    unsigned char byte = buf[-static_cast<std::ptrdiff_t>(i)];
  188|    992|    found_leading_bytes = ((byte & 0b11000000) != 0b10000000);
  189|    992|    if (found_leading_bytes) {
  ------------------
  |  Branch (189:9): [True: 868, False: 124]
  ------------------
  190|    868|      if (i > 0 && byte < 128) {
  ------------------
  |  Branch (190:11): [True: 64, False: 804]
  |  Branch (190:20): [True: 64, False: 0]
  ------------------
  191|       |        // If we had to go back and the leading byte is ascii
  192|       |        // then we can stop right away.
  193|     64|        return result(error_code::TOO_LONG, 0 - i + 1);
  194|     64|      }
  195|    804|      buf -= i;
  196|    804|      extra_len = i;
  197|    804|      break;
  198|    868|    }
  199|    992|  }
  200|       |  //
  201|       |  // It is possible for this function to return a negative count in its result.
  202|       |  // C++ Standard Section 18.1 defines size_t is in <cstddef> which is described
  203|       |  // in C Standard as <stddef.h>. C Standard Section 4.1.5 defines size_t as an
  204|       |  // unsigned integral type of the result of the sizeof operator
  205|       |  //
  206|       |  // An unsigned type will simply wrap round arithmetically (well defined).
  207|       |  //
  208|    864|  if (!found_leading_bytes) {
  ------------------
  |  Branch (208:7): [True: 60, False: 804]
  ------------------
  209|       |    // If how_far_back == 3, we may have four consecutive continuation bytes!!!
  210|       |    // [....] [continuation] [continuation] [continuation] | [buf is
  211|       |    // continuation] Or we possibly have a stream that does not start with a
  212|       |    // leading byte.
  213|     60|    return result(error_code::TOO_LONG, 0 - how_far_back);
  214|     60|  }
  215|    804|  result res = convert_with_errors(buf, len + extra_len, latin1_output);
  216|    804|  if (res.error) {
  ------------------
  |  Branch (216:7): [True: 596, False: 208]
  ------------------
  217|    596|    res.count -= extra_len;
  218|    596|  }
  219|    804|  return res;
  220|    864|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf8_to_latin119convert_with_errorsIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEENS_6resultES6_mPc:
   92|  1.90k|                                               char *latin_output) {
   93|  1.90k|  size_t pos = 0;
   94|  1.90k|  char *start{latin_output};
   95|       |
   96|  1.76M|  while (pos < len) {
  ------------------
  |  Branch (96:10): [True: 1.76M, False: 592]
  ------------------
   97|       |#if SIMDUTF_CPLUSPLUS23
   98|       |    if !consteval
   99|       |#endif
  100|  1.76M|    {
  101|       |      // try to convert the next block of 16 ASCII bytes
  102|  1.76M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (102:11): [True: 1.75M, False: 6.95k]
  ------------------
  103|       |                             // they are ascii
  104|  1.75M|        uint64_t v1;
  105|  1.75M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
  106|  1.75M|        uint64_t v2;
  107|  1.75M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  108|  1.75M|        uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000
  109|       |                             // 1000 1000...etc
  110|  1.75M|        if ((v & 0x8080808080808080) ==
  ------------------
  |  Branch (110:13): [True: 1.52M, False: 229k]
  ------------------
  111|  1.75M|            0) { // if NONE of these are set, e.g. all of them are zero, then
  112|       |                 // everything is ASCII
  113|  1.52M|          size_t final_pos = pos + 16;
  114|  25.9M|          while (pos < final_pos) {
  ------------------
  |  Branch (114:18): [True: 24.4M, False: 1.52M]
  ------------------
  115|  24.4M|            *latin_output++ = char(data[pos]);
  116|  24.4M|            pos++;
  117|  24.4M|          }
  118|  1.52M|          continue;
  119|  1.52M|        }
  120|  1.75M|      }
  121|  1.76M|    }
  122|       |    // suppose it is not an all ASCII byte sequence
  123|   236k|    uint8_t leading_byte = data[pos]; // leading byte
  124|   236k|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (124:9): [True: 205k, False: 30.7k]
  ------------------
  125|       |      // converting one ASCII byte !!!
  126|   205k|      *latin_output++ = char(leading_byte);
  127|   205k|      pos++;
  128|   205k|    } else if ((leading_byte & 0b11100000) ==
  ------------------
  |  Branch (128:16): [True: 29.8k, False: 854]
  ------------------
  129|  30.7k|               0b11000000) { // the first three bits indicate:
  130|       |      // We have a two-byte UTF-8
  131|  29.8k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (131:11): [True: 84, False: 29.7k]
  ------------------
  132|     84|        return result(error_code::TOO_SHORT, pos);
  133|     84|      } // minimal bound checking
  134|  29.7k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (134:11): [True: 270, False: 29.5k]
  ------------------
  135|    270|        return result(error_code::TOO_SHORT, pos);
  136|    270|      } // checks if the next byte is a valid continuation byte in UTF-8. A
  137|       |        // valid continuation byte starts with 10.
  138|       |      // range check -
  139|  29.5k|      uint32_t code_point =
  140|  29.5k|          (leading_byte & 0b00011111) << 6 |
  141|  29.5k|          (data[pos + 1] &
  142|  29.5k|           0b00111111); // assembles the Unicode code point from the two bytes.
  143|       |                        // It does this by discarding the leading 110 and 10
  144|       |                        // bits from the two bytes, shifting the remaining bits
  145|       |                        // of the first byte, and then combining the results
  146|       |                        // with a bitwise OR operation.
  147|  29.5k|      if (code_point < 0x80) {
  ------------------
  |  Branch (147:11): [True: 18, False: 29.4k]
  ------------------
  148|     18|        return result(error_code::OVERLONG, pos);
  149|     18|      }
  150|  29.4k|      if (0xFF < code_point) {
  ------------------
  |  Branch (150:11): [True: 84, False: 29.4k]
  ------------------
  151|     84|        return result(error_code::TOO_LARGE, pos);
  152|     84|      } // We only care about the range 129-255 which is Non-ASCII latin1
  153|       |        // characters
  154|  29.4k|      *latin_output++ = char(code_point);
  155|  29.4k|      pos += 2;
  156|  29.4k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (156:16): [True: 132, False: 722]
  ------------------
  157|       |      // We have a three-byte UTF-8
  158|    132|      return result(error_code::TOO_LARGE, pos);
  159|    722|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (159:16): [True: 120, False: 602]
  ------------------
  160|       |      // we have a 4-byte UTF-8 word.
  161|    120|      return result(error_code::TOO_LARGE, pos);
  162|    602|    } else {
  163|       |      // we either have too many continuation bytes or an invalid leading byte
  164|    602|      if ((leading_byte & 0b11000000) == 0b10000000) {
  ------------------
  |  Branch (164:11): [True: 326, False: 276]
  ------------------
  165|    326|        return result(error_code::TOO_LONG, pos);
  166|    326|      }
  167|       |
  168|    276|      return result(error_code::HEADER_BITS, pos);
  169|    602|    }
  170|   236k|  }
  171|    592|  return result(error_code::SUCCESS, latin_output - start);
  172|  1.90k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf8_to_latin17convertIPKcPcQaasr7simdutf6detailE22indexes_into_byte_likeIT_Esr7simdutf6detailE22indexes_into_byte_likeIT0_EEEmS7_mS8_:
   17|  1.53k|                                   OutputPtr latin_output) {
   18|  1.53k|  size_t pos = 0;
   19|  1.53k|  auto start = latin_output;
   20|       |
   21|  1.21M|  while (pos < len) {
  ------------------
  |  Branch (21:10): [True: 1.21M, False: 432]
  ------------------
   22|       |#if SIMDUTF_CPLUSPLUS23
   23|       |    if !consteval
   24|       |#endif
   25|  1.21M|    {
   26|       |      // try to convert the next block of 16 ASCII bytes
   27|  1.21M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (27:11): [True: 1.20M, False: 4.43k]
  ------------------
   28|       |                             // they are ascii
   29|  1.20M|        uint64_t v1;
   30|  1.20M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
   31|  1.20M|        uint64_t v2;
   32|  1.20M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   33|  1.20M|        uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000
   34|       |                             // 1000 1000 .... etc
   35|  1.20M|        if ((v & 0x8080808080808080) ==
  ------------------
  |  Branch (35:13): [True: 1.18M, False: 25.4k]
  ------------------
   36|  1.20M|            0) { // if NONE of these are set, e.g. all of them are zero, then
   37|       |                 // everything is ASCII
   38|  1.18M|          size_t final_pos = pos + 16;
   39|  20.0M|          while (pos < final_pos) {
  ------------------
  |  Branch (39:18): [True: 18.9M, False: 1.18M]
  ------------------
   40|  18.9M|            *latin_output++ = char(data[pos]);
   41|  18.9M|            pos++;
   42|  18.9M|          }
   43|  1.18M|          continue;
   44|  1.18M|        }
   45|  1.20M|      }
   46|  1.21M|    }
   47|       |
   48|       |    // suppose it is not an all ASCII byte sequence
   49|  29.8k|    uint8_t leading_byte = data[pos]; // leading byte
   50|  29.8k|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (50:9): [True: 25.1k, False: 4.75k]
  ------------------
   51|       |      // converting one ASCII byte !!!
   52|  25.1k|      *latin_output++ = char(leading_byte);
   53|  25.1k|      pos++;
   54|  25.1k|    } else if ((leading_byte & 0b11100000) ==
  ------------------
  |  Branch (54:16): [True: 4.03k, False: 720]
  ------------------
   55|  4.75k|               0b11000000) { // the first three bits indicate:
   56|       |      // We have a two-byte UTF-8
   57|  4.03k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (57:11): [True: 30, False: 4.00k]
  ------------------
   58|     30|        return 0;
   59|     30|      } // minimal bound checking
   60|  4.00k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (60:11): [True: 168, False: 3.83k]
  ------------------
   61|    168|        return 0;
   62|    168|      } // checks if the next byte is a valid continuation byte in UTF-8. A
   63|       |        // valid continuation byte starts with 10.
   64|       |      // range check -
   65|  3.83k|      uint32_t code_point =
   66|  3.83k|          (leading_byte & 0b00011111) << 6 |
   67|  3.83k|          (data[pos + 1] &
   68|  3.83k|           0b00111111); // assembles the Unicode code point from the two bytes.
   69|       |                        // It does this by discarding the leading 110 and 10
   70|       |                        // bits from the two bytes, shifting the remaining bits
   71|       |                        // of the first byte, and then combining the results
   72|       |                        // with a bitwise OR operation.
   73|  3.83k|      if (code_point < 0x80 || 0xFF < code_point) {
  ------------------
  |  Branch (73:11): [True: 72, False: 3.76k]
  |  Branch (73:32): [True: 108, False: 3.65k]
  ------------------
   74|    180|        return 0; // We only care about the range 129-255 which is Non-ASCII
   75|       |                  // latin1 characters. A code_point beneath 0x80 is invalid as
   76|       |                  // it is already covered by bytes whose leading bit is zero.
   77|    180|      }
   78|  3.65k|      *latin_output++ = char(code_point);
   79|  3.65k|      pos += 2;
   80|  3.65k|    } else {
   81|    720|      return 0;
   82|    720|    }
   83|  29.8k|  }
   84|    432|  return latin_output - start;
   85|  1.53k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf167convertILNS_10endiannessE0EPKcQsr7simdutf6detailE22indexes_into_byte_likeIT0_EEEmS7_mPDs:
   16|  2.07k|                                   char16_t *utf16_output) {
   17|  2.07k|  size_t pos = 0;
   18|  2.07k|  char16_t *start{utf16_output};
   19|  2.38M|  while (pos < len) {
  ------------------
  |  Branch (19:10): [True: 2.38M, False: 608]
  ------------------
   20|       |#if SIMDUTF_CPLUSPLUS23
   21|       |    if !consteval
   22|       |#endif
   23|       |    // try to convert the next block of 16 ASCII bytes
   24|  2.38M|    {
   25|  2.38M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (25:11): [True: 2.37M, False: 4.76k]
  ------------------
   26|       |                             // they are ascii
   27|  2.37M|        uint64_t v1;
   28|  2.37M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
   29|  2.37M|        uint64_t v2;
   30|  2.37M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   31|  2.37M|        uint64_t v{v1 | v2};
   32|  2.37M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (32:13): [True: 1.32M, False: 1.04M]
  ------------------
   33|  1.32M|          size_t final_pos = pos + 16;
   34|  22.6M|          while (pos < final_pos) {
  ------------------
  |  Branch (34:18): [True: 21.2M, False: 1.32M]
  ------------------
   35|  21.2M|            *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (35:31): [Folded, False: 21.2M]
  ------------------
   36|  21.2M|                                  ? char16_t(u16_swap_bytes(data[pos]))
   37|  21.2M|                                  : char16_t(data[pos]);
   38|  21.2M|            pos++;
   39|  21.2M|          }
   40|  1.32M|          continue;
   41|  1.32M|        }
   42|  2.37M|      }
   43|  2.38M|    }
   44|       |
   45|  1.05M|    uint8_t leading_byte = data[pos]; // leading byte
   46|  1.05M|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (46:9): [True: 855k, False: 195k]
  ------------------
   47|       |      // converting one ASCII byte !!!
   48|   855k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (48:25): [Folded, False: 855k]
  ------------------
   49|   855k|                            ? char16_t(u16_swap_bytes(leading_byte))
   50|   855k|                            : char16_t(leading_byte);
   51|   855k|      pos++;
   52|   855k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (52:16): [True: 118k, False: 77.6k]
  ------------------
   53|       |      // We have a two-byte UTF-8, it should become
   54|       |      // a single UTF-16 word.
   55|   118k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (55:11): [True: 18, False: 118k]
  ------------------
   56|     18|        return 0;
   57|     18|      } // minimal bound checking
   58|   118k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (58:11): [True: 188, False: 117k]
  ------------------
   59|    188|        return 0;
   60|    188|      }
   61|       |      // range check
   62|   117k|      uint32_t code_point =
   63|   117k|          (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
   64|   117k|      if (code_point < 0x80) {
  ------------------
  |  Branch (64:11): [True: 26, False: 117k]
  ------------------
   65|     26|        return 0;
   66|     26|      }
   67|       |      if constexpr (!match_system(big_endian)) {
   68|       |        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
   69|       |      }
   70|   117k|      *utf16_output++ = char16_t(code_point);
   71|   117k|      pos += 2;
   72|   117k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (72:16): [True: 68.8k, False: 8.72k]
  ------------------
   73|       |      // We have a three-byte UTF-8, it should become
   74|       |      // a single UTF-16 word.
   75|  68.8k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (75:11): [True: 24, False: 68.8k]
  ------------------
   76|     24|        return 0;
   77|     24|      } // minimal bound checking
   78|       |
   79|  68.8k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (79:11): [True: 100, False: 68.7k]
  ------------------
   80|    100|        return 0;
   81|    100|      }
   82|  68.7k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (82:11): [True: 40, False: 68.7k]
  ------------------
   83|     40|        return 0;
   84|     40|      }
   85|       |      // range check
   86|  68.7k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
   87|  68.7k|                            (data[pos + 1] & 0b00111111) << 6 |
   88|  68.7k|                            (data[pos + 2] & 0b00111111);
   89|  68.7k|      if (code_point < 0x800 || (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (89:11): [True: 56, False: 68.6k]
  |  Branch (89:34): [True: 688, False: 67.9k]
  |  Branch (89:57): [True: 22, False: 666]
  ------------------
   90|     78|        return 0;
   91|     78|      }
   92|       |      if constexpr (!match_system(big_endian)) {
   93|       |        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
   94|       |      }
   95|  68.6k|      *utf16_output++ = char16_t(code_point);
   96|  68.6k|      pos += 3;
   97|  68.6k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (97:16): [True: 7.94k, False: 782]
  ------------------
   98|       |      // we have a 4-byte UTF-8 word.
   99|  7.94k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (99:11): [True: 24, False: 7.91k]
  ------------------
  100|     24|        return 0;
  101|     24|      } // minimal bound checking
  102|  7.91k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (102:11): [True: 54, False: 7.86k]
  ------------------
  103|     54|        return 0;
  104|     54|      }
  105|  7.86k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (105:11): [True: 38, False: 7.82k]
  ------------------
  106|     38|        return 0;
  107|     38|      }
  108|  7.82k|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (108:11): [True: 32, False: 7.79k]
  ------------------
  109|     32|        return 0;
  110|     32|      }
  111|       |
  112|       |      // range check
  113|  7.79k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  114|  7.79k|                            (data[pos + 1] & 0b00111111) << 12 |
  115|  7.79k|                            (data[pos + 2] & 0b00111111) << 6 |
  116|  7.79k|                            (data[pos + 3] & 0b00111111);
  117|  7.79k|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (117:11): [True: 20, False: 7.77k]
  |  Branch (117:35): [True: 38, False: 7.73k]
  ------------------
  118|     58|        return 0;
  119|     58|      }
  120|  7.73k|      code_point -= 0x10000;
  121|       |      // The mask is a no-op (code_point < 0x100000). It stops clang 18's SLP
  122|       |      // vectorizer on RISC-V V from narrowing code_point to 16 bits before
  123|       |      // the shift, which drops bits 16-19 of the code point.
  124|  7.73k|      uint16_t high_surrogate = uint16_t(0xD800 + ((code_point >> 10) & 0x3FF));
  125|  7.73k|      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
  126|       |      if constexpr (!match_system(big_endian)) {
  127|       |        high_surrogate = u16_swap_bytes(high_surrogate);
  128|       |        low_surrogate = u16_swap_bytes(low_surrogate);
  129|       |      }
  130|  7.73k|      *utf16_output++ = char16_t(high_surrogate);
  131|  7.73k|      *utf16_output++ = char16_t(low_surrogate);
  132|  7.73k|      pos += 4;
  133|  7.73k|    } else {
  134|    782|      return 0;
  135|    782|    }
  136|  1.05M|  }
  137|    608|  return utf16_output - start;
  138|  2.07k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf167convertILNS_10endiannessE1EPKcQsr7simdutf6detailE22indexes_into_byte_likeIT0_EEEmS7_mPDs:
   16|  2.14k|                                   char16_t *utf16_output) {
   17|  2.14k|  size_t pos = 0;
   18|  2.14k|  char16_t *start{utf16_output};
   19|  2.54M|  while (pos < len) {
  ------------------
  |  Branch (19:10): [True: 2.54M, False: 522]
  ------------------
   20|       |#if SIMDUTF_CPLUSPLUS23
   21|       |    if !consteval
   22|       |#endif
   23|       |    // try to convert the next block of 16 ASCII bytes
   24|  2.54M|    {
   25|  2.54M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (25:11): [True: 2.53M, False: 4.22k]
  ------------------
   26|       |                             // they are ascii
   27|  2.53M|        uint64_t v1;
   28|  2.53M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
   29|  2.53M|        uint64_t v2;
   30|  2.53M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   31|  2.53M|        uint64_t v{v1 | v2};
   32|  2.53M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (32:13): [True: 1.28M, False: 1.25M]
  ------------------
   33|  1.28M|          size_t final_pos = pos + 16;
   34|  21.8M|          while (pos < final_pos) {
  ------------------
  |  Branch (34:18): [True: 20.5M, False: 1.28M]
  ------------------
   35|  20.5M|            *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (35:31): [True: 20.5M, Folded]
  ------------------
   36|  20.5M|                                  ? char16_t(u16_swap_bytes(data[pos]))
   37|  20.5M|                                  : char16_t(data[pos]);
   38|  20.5M|            pos++;
   39|  20.5M|          }
   40|  1.28M|          continue;
   41|  1.28M|        }
   42|  2.53M|      }
   43|  2.54M|    }
   44|       |
   45|  1.25M|    uint8_t leading_byte = data[pos]; // leading byte
   46|  1.25M|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (46:9): [True: 956k, False: 300k]
  ------------------
   47|       |      // converting one ASCII byte !!!
   48|   956k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (48:25): [True: 956k, Folded]
  ------------------
   49|   956k|                            ? char16_t(u16_swap_bytes(leading_byte))
   50|   956k|                            : char16_t(leading_byte);
   51|   956k|      pos++;
   52|   956k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (52:16): [True: 207k, False: 92.6k]
  ------------------
   53|       |      // We have a two-byte UTF-8, it should become
   54|       |      // a single UTF-16 word.
   55|   207k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (55:11): [True: 72, False: 207k]
  ------------------
   56|     72|        return 0;
   57|     72|      } // minimal bound checking
   58|   207k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (58:11): [True: 158, False: 207k]
  ------------------
   59|    158|        return 0;
   60|    158|      }
   61|       |      // range check
   62|   207k|      uint32_t code_point =
   63|   207k|          (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
   64|   207k|      if (code_point < 0x80) {
  ------------------
  |  Branch (64:11): [True: 20, False: 207k]
  ------------------
   65|     20|        return 0;
   66|     20|      }
   67|   207k|      if constexpr (!match_system(big_endian)) {
   68|   207k|        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
   69|   207k|      }
   70|   207k|      *utf16_output++ = char16_t(code_point);
   71|   207k|      pos += 2;
   72|   207k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (72:16): [True: 68.0k, False: 24.5k]
  ------------------
   73|       |      // We have a three-byte UTF-8, it should become
   74|       |      // a single UTF-16 word.
   75|  68.0k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (75:11): [True: 36, False: 68.0k]
  ------------------
   76|     36|        return 0;
   77|     36|      } // minimal bound checking
   78|       |
   79|  68.0k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (79:11): [True: 132, False: 67.9k]
  ------------------
   80|    132|        return 0;
   81|    132|      }
   82|  67.9k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (82:11): [True: 70, False: 67.8k]
  ------------------
   83|     70|        return 0;
   84|     70|      }
   85|       |      // range check
   86|  67.8k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
   87|  67.8k|                            (data[pos + 1] & 0b00111111) << 6 |
   88|  67.8k|                            (data[pos + 2] & 0b00111111);
   89|  67.8k|      if (code_point < 0x800 || (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (89:11): [True: 50, False: 67.7k]
  |  Branch (89:34): [True: 4.38k, False: 63.3k]
  |  Branch (89:57): [True: 20, False: 4.36k]
  ------------------
   90|     70|        return 0;
   91|     70|      }
   92|  67.7k|      if constexpr (!match_system(big_endian)) {
   93|  67.7k|        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
   94|  67.7k|      }
   95|  67.7k|      *utf16_output++ = char16_t(code_point);
   96|  67.7k|      pos += 3;
   97|  67.7k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (97:16): [True: 23.8k, False: 718]
  ------------------
   98|       |      // we have a 4-byte UTF-8 word.
   99|  23.8k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (99:11): [True: 30, False: 23.7k]
  ------------------
  100|     30|        return 0;
  101|     30|      } // minimal bound checking
  102|  23.7k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (102:11): [True: 152, False: 23.6k]
  ------------------
  103|    152|        return 0;
  104|    152|      }
  105|  23.6k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (105:11): [True: 46, False: 23.5k]
  ------------------
  106|     46|        return 0;
  107|     46|      }
  108|  23.5k|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (108:11): [True: 20, False: 23.5k]
  ------------------
  109|     20|        return 0;
  110|     20|      }
  111|       |
  112|       |      // range check
  113|  23.5k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  114|  23.5k|                            (data[pos + 1] & 0b00111111) << 12 |
  115|  23.5k|                            (data[pos + 2] & 0b00111111) << 6 |
  116|  23.5k|                            (data[pos + 3] & 0b00111111);
  117|  23.5k|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (117:11): [True: 40, False: 23.5k]
  |  Branch (117:35): [True: 62, False: 23.4k]
  ------------------
  118|    102|        return 0;
  119|    102|      }
  120|  23.4k|      code_point -= 0x10000;
  121|       |      // The mask is a no-op (code_point < 0x100000). It stops clang 18's SLP
  122|       |      // vectorizer on RISC-V V from narrowing code_point to 16 bits before
  123|       |      // the shift, which drops bits 16-19 of the code point.
  124|  23.4k|      uint16_t high_surrogate = uint16_t(0xD800 + ((code_point >> 10) & 0x3FF));
  125|  23.4k|      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
  126|  23.4k|      if constexpr (!match_system(big_endian)) {
  127|  23.4k|        high_surrogate = u16_swap_bytes(high_surrogate);
  128|  23.4k|        low_surrogate = u16_swap_bytes(low_surrogate);
  129|  23.4k|      }
  130|  23.4k|      *utf16_output++ = char16_t(high_surrogate);
  131|  23.4k|      *utf16_output++ = char16_t(low_surrogate);
  132|  23.4k|      pos += 4;
  133|  23.4k|    } else {
  134|    718|      return 0;
  135|    718|    }
  136|  1.25M|  }
  137|    522|  return utf16_output - start;
  138|  2.14k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf1619convert_with_errorsILNS_10endiannessE0EPKcQsr7simdutf6detailE22indexes_into_byte_likeIT0_EEENS_6resultES7_mPDs:
  145|  2.07k|                                               char16_t *utf16_output) {
  146|  2.07k|  size_t pos = 0;
  147|  2.07k|  char16_t *start{utf16_output};
  148|  2.47M|  while (pos < len) {
  ------------------
  |  Branch (148:10): [True: 2.47M, False: 640]
  ------------------
  149|       |#if SIMDUTF_CPLUSPLUS23
  150|       |    if !consteval
  151|       |#endif
  152|  2.47M|    {
  153|       |      // try to convert the next block of 16 ASCII bytes
  154|  2.47M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (154:11): [True: 2.46M, False: 5.70k]
  ------------------
  155|       |                             // they are ascii
  156|  2.46M|        uint64_t v1;
  157|  2.46M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
  158|  2.46M|        uint64_t v2;
  159|  2.46M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  160|  2.46M|        uint64_t v{v1 | v2};
  161|  2.46M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (161:13): [True: 1.22M, False: 1.24M]
  ------------------
  162|  1.22M|          size_t final_pos = pos + 16;
  163|  20.7M|          while (pos < final_pos) {
  ------------------
  |  Branch (163:18): [True: 19.5M, False: 1.22M]
  ------------------
  164|  19.5M|            const char16_t byte = uint8_t(data[pos]);
  165|  19.5M|            *utf16_output++ =
  166|  19.5M|                !match_system(big_endian) ? u16_swap_bytes(byte) : byte;
  ------------------
  |  Branch (166:17): [Folded, False: 19.5M]
  ------------------
  167|  19.5M|            pos++;
  168|  19.5M|          }
  169|  1.22M|          continue;
  170|  1.22M|        }
  171|  2.46M|      }
  172|  2.47M|    }
  173|       |
  174|  1.25M|    auto leading_byte = uint8_t(data[pos]); // leading byte
  175|  1.25M|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (175:9): [True: 1.00M, False: 250k]
  ------------------
  176|       |      // converting one ASCII byte !!!
  177|  1.00M|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (177:25): [Folded, False: 1.00M]
  ------------------
  178|  1.00M|                            ? char16_t(u16_swap_bytes(leading_byte))
  179|  1.00M|                            : char16_t(leading_byte);
  180|  1.00M|      pos++;
  181|  1.00M|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (181:16): [True: 176k, False: 74.0k]
  ------------------
  182|       |      // We have a two-byte UTF-8, it should become
  183|       |      // a single UTF-16 word.
  184|   176k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (184:11): [True: 66, False: 176k]
  ------------------
  185|     66|        return result(error_code::TOO_SHORT, pos);
  186|     66|      } // minimal bound checking
  187|   176k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (187:11): [True: 198, False: 176k]
  ------------------
  188|    198|        return result(error_code::TOO_SHORT, pos);
  189|    198|      }
  190|       |      // range check
  191|   176k|      uint32_t code_point = (leading_byte & 0b00011111) << 6 |
  192|   176k|                            (uint8_t(data[pos + 1]) & 0b00111111);
  193|   176k|      if (code_point < 0x80) {
  ------------------
  |  Branch (193:11): [True: 30, False: 176k]
  ------------------
  194|     30|        return result(error_code::OVERLONG, pos);
  195|     30|      }
  196|       |      if constexpr (!match_system(big_endian)) {
  197|       |        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
  198|       |      }
  199|   176k|      *utf16_output++ = char16_t(code_point);
  200|   176k|      pos += 2;
  201|   176k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (201:16): [True: 55.3k, False: 18.7k]
  ------------------
  202|       |      // We have a three-byte UTF-8, it should become
  203|       |      // a single UTF-16 word.
  204|  55.3k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (204:11): [True: 12, False: 55.3k]
  ------------------
  205|     12|        return result(error_code::TOO_SHORT, pos);
  206|     12|      } // minimal bound checking
  207|       |
  208|  55.3k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (208:11): [True: 102, False: 55.2k]
  ------------------
  209|    102|        return result(error_code::TOO_SHORT, pos);
  210|    102|      }
  211|  55.2k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (211:11): [True: 30, False: 55.1k]
  ------------------
  212|     30|        return result(error_code::TOO_SHORT, pos);
  213|     30|      }
  214|       |      // range check
  215|  55.1k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
  216|  55.1k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 6 |
  217|  55.1k|                            (uint8_t(data[pos + 2]) & 0b00111111);
  218|  55.1k|      if (code_point < 0x800) {
  ------------------
  |  Branch (218:11): [True: 30, False: 55.1k]
  ------------------
  219|     30|        return result(error_code::OVERLONG, pos);
  220|     30|      }
  221|  55.1k|      if (0xd7ff < code_point && code_point < 0xe000) {
  ------------------
  |  Branch (221:11): [True: 3.00k, False: 52.1k]
  |  Branch (221:34): [True: 12, False: 2.99k]
  ------------------
  222|     12|        return result(error_code::SURROGATE, pos);
  223|     12|      }
  224|       |      if constexpr (!match_system(big_endian)) {
  225|       |        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
  226|       |      }
  227|  55.1k|      *utf16_output++ = char16_t(code_point);
  228|  55.1k|      pos += 3;
  229|  55.1k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (229:16): [True: 18.0k, False: 682]
  ------------------
  230|       |      // we have a 4-byte UTF-8 word.
  231|  18.0k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (231:11): [True: 36, False: 18.0k]
  ------------------
  232|     36|        return result(error_code::TOO_SHORT, pos);
  233|     36|      } // minimal bound checking
  234|  18.0k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (234:11): [True: 78, False: 17.9k]
  ------------------
  235|     78|        return result(error_code::TOO_SHORT, pos);
  236|     78|      }
  237|  17.9k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (237:11): [True: 54, False: 17.8k]
  ------------------
  238|     54|        return result(error_code::TOO_SHORT, pos);
  239|     54|      }
  240|  17.8k|      if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (240:11): [True: 30, False: 17.8k]
  ------------------
  241|     30|        return result(error_code::TOO_SHORT, pos);
  242|     30|      }
  243|       |
  244|       |      // range check
  245|  17.8k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  246|  17.8k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 12 |
  247|  17.8k|                            (uint8_t(data[pos + 2]) & 0b00111111) << 6 |
  248|  17.8k|                            (uint8_t(data[pos + 3]) & 0b00111111);
  249|  17.8k|      if (code_point <= 0xffff) {
  ------------------
  |  Branch (249:11): [True: 24, False: 17.8k]
  ------------------
  250|     24|        return result(error_code::OVERLONG, pos);
  251|     24|      }
  252|  17.8k|      if (0x10ffff < code_point) {
  ------------------
  |  Branch (252:11): [True: 54, False: 17.7k]
  ------------------
  253|     54|        return result(error_code::TOO_LARGE, pos);
  254|     54|      }
  255|  17.7k|      code_point -= 0x10000;
  256|       |      // The mask is a no-op (code_point < 0x100000). It stops clang 18's SLP
  257|       |      // vectorizer on RISC-V V from narrowing code_point to 16 bits before
  258|       |      // the shift, which drops bits 16-19 of the code point.
  259|  17.7k|      uint16_t high_surrogate = uint16_t(0xD800 + ((code_point >> 10) & 0x3FF));
  260|  17.7k|      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
  261|       |      if constexpr (!match_system(big_endian)) {
  262|       |        high_surrogate = u16_swap_bytes(high_surrogate);
  263|       |        low_surrogate = u16_swap_bytes(low_surrogate);
  264|       |      }
  265|  17.7k|      *utf16_output++ = char16_t(high_surrogate);
  266|  17.7k|      *utf16_output++ = char16_t(low_surrogate);
  267|  17.7k|      pos += 4;
  268|  17.7k|    } else {
  269|       |      // we either have too many continuation bytes or an invalid leading byte
  270|    682|      if ((leading_byte & 0b11000000) == 0b10000000) {
  ------------------
  |  Branch (270:11): [True: 340, False: 342]
  ------------------
  271|    340|        return result(error_code::TOO_LONG, pos);
  272|    342|      } else {
  273|    342|        return result(error_code::HEADER_BITS, pos);
  274|    342|      }
  275|    682|    }
  276|  1.25M|  }
  277|    640|  return result(error_code::SUCCESS, utf16_output - start);
  278|  2.07k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf1619convert_with_errorsILNS_10endiannessE1EPKcQsr7simdutf6detailE22indexes_into_byte_likeIT0_EEENS_6resultES7_mPDs:
  145|  2.58k|                                               char16_t *utf16_output) {
  146|  2.58k|  size_t pos = 0;
  147|  2.58k|  char16_t *start{utf16_output};
  148|  3.78M|  while (pos < len) {
  ------------------
  |  Branch (148:10): [True: 3.78M, False: 872]
  ------------------
  149|       |#if SIMDUTF_CPLUSPLUS23
  150|       |    if !consteval
  151|       |#endif
  152|  3.78M|    {
  153|       |      // try to convert the next block of 16 ASCII bytes
  154|  3.78M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (154:11): [True: 3.77M, False: 9.03k]
  ------------------
  155|       |                             // they are ascii
  156|  3.77M|        uint64_t v1;
  157|  3.77M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
  158|  3.77M|        uint64_t v2;
  159|  3.77M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  160|  3.77M|        uint64_t v{v1 | v2};
  161|  3.77M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (161:13): [True: 1.72M, False: 2.05M]
  ------------------
  162|  1.72M|          size_t final_pos = pos + 16;
  163|  29.2M|          while (pos < final_pos) {
  ------------------
  |  Branch (163:18): [True: 27.5M, False: 1.72M]
  ------------------
  164|  27.5M|            const char16_t byte = uint8_t(data[pos]);
  165|  27.5M|            *utf16_output++ =
  166|  27.5M|                !match_system(big_endian) ? u16_swap_bytes(byte) : byte;
  ------------------
  |  Branch (166:17): [True: 27.5M, Folded]
  ------------------
  167|  27.5M|            pos++;
  168|  27.5M|          }
  169|  1.72M|          continue;
  170|  1.72M|        }
  171|  3.77M|      }
  172|  3.78M|    }
  173|       |
  174|  2.06M|    auto leading_byte = uint8_t(data[pos]); // leading byte
  175|  2.06M|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (175:9): [True: 1.63M, False: 425k]
  ------------------
  176|       |      // converting one ASCII byte !!!
  177|  1.63M|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (177:25): [True: 1.63M, Folded]
  ------------------
  178|  1.63M|                            ? char16_t(u16_swap_bytes(leading_byte))
  179|  1.63M|                            : char16_t(leading_byte);
  180|  1.63M|      pos++;
  181|  1.63M|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (181:16): [True: 282k, False: 142k]
  ------------------
  182|       |      // We have a two-byte UTF-8, it should become
  183|       |      // a single UTF-16 word.
  184|   282k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (184:11): [True: 78, False: 282k]
  ------------------
  185|     78|        return result(error_code::TOO_SHORT, pos);
  186|     78|      } // minimal bound checking
  187|   282k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (187:11): [True: 210, False: 282k]
  ------------------
  188|    210|        return result(error_code::TOO_SHORT, pos);
  189|    210|      }
  190|       |      // range check
  191|   282k|      uint32_t code_point = (leading_byte & 0b00011111) << 6 |
  192|   282k|                            (uint8_t(data[pos + 1]) & 0b00111111);
  193|   282k|      if (code_point < 0x80) {
  ------------------
  |  Branch (193:11): [True: 12, False: 282k]
  ------------------
  194|     12|        return result(error_code::OVERLONG, pos);
  195|     12|      }
  196|   282k|      if constexpr (!match_system(big_endian)) {
  197|   282k|        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
  198|   282k|      }
  199|   282k|      *utf16_output++ = char16_t(code_point);
  200|   282k|      pos += 2;
  201|   282k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (201:16): [True: 123k, False: 18.9k]
  ------------------
  202|       |      // We have a three-byte UTF-8, it should become
  203|       |      // a single UTF-16 word.
  204|   123k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (204:11): [True: 42, False: 123k]
  ------------------
  205|     42|        return result(error_code::TOO_SHORT, pos);
  206|     42|      } // minimal bound checking
  207|       |
  208|   123k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (208:11): [True: 96, False: 123k]
  ------------------
  209|     96|        return result(error_code::TOO_SHORT, pos);
  210|     96|      }
  211|   123k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (211:11): [True: 42, False: 123k]
  ------------------
  212|     42|        return result(error_code::TOO_SHORT, pos);
  213|     42|      }
  214|       |      // range check
  215|   123k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
  216|   123k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 6 |
  217|   123k|                            (uint8_t(data[pos + 2]) & 0b00111111);
  218|   123k|      if (code_point < 0x800) {
  ------------------
  |  Branch (218:11): [True: 24, False: 123k]
  ------------------
  219|     24|        return result(error_code::OVERLONG, pos);
  220|     24|      }
  221|   123k|      if (0xd7ff < code_point && code_point < 0xe000) {
  ------------------
  |  Branch (221:11): [True: 4.16k, False: 119k]
  |  Branch (221:34): [True: 6, False: 4.15k]
  ------------------
  222|      6|        return result(error_code::SURROGATE, pos);
  223|      6|      }
  224|   123k|      if constexpr (!match_system(big_endian)) {
  225|   123k|        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
  226|   123k|      }
  227|   123k|      *utf16_output++ = char16_t(code_point);
  228|   123k|      pos += 3;
  229|   123k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (229:16): [True: 18.0k, False: 876]
  ------------------
  230|       |      // we have a 4-byte UTF-8 word.
  231|  18.0k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (231:11): [True: 72, False: 17.9k]
  ------------------
  232|     72|        return result(error_code::TOO_SHORT, pos);
  233|     72|      } // minimal bound checking
  234|  17.9k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (234:11): [True: 102, False: 17.8k]
  ------------------
  235|    102|        return result(error_code::TOO_SHORT, pos);
  236|    102|      }
  237|  17.8k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (237:11): [True: 42, False: 17.8k]
  ------------------
  238|     42|        return result(error_code::TOO_SHORT, pos);
  239|     42|      }
  240|  17.8k|      if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (240:11): [True: 24, False: 17.7k]
  ------------------
  241|     24|        return result(error_code::TOO_SHORT, pos);
  242|     24|      }
  243|       |
  244|       |      // range check
  245|  17.7k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  246|  17.7k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 12 |
  247|  17.7k|                            (uint8_t(data[pos + 2]) & 0b00111111) << 6 |
  248|  17.7k|                            (uint8_t(data[pos + 3]) & 0b00111111);
  249|  17.7k|      if (code_point <= 0xffff) {
  ------------------
  |  Branch (249:11): [True: 36, False: 17.7k]
  ------------------
  250|     36|        return result(error_code::OVERLONG, pos);
  251|     36|      }
  252|  17.7k|      if (0x10ffff < code_point) {
  ------------------
  |  Branch (252:11): [True: 48, False: 17.7k]
  ------------------
  253|     48|        return result(error_code::TOO_LARGE, pos);
  254|     48|      }
  255|  17.7k|      code_point -= 0x10000;
  256|       |      // The mask is a no-op (code_point < 0x100000). It stops clang 18's SLP
  257|       |      // vectorizer on RISC-V V from narrowing code_point to 16 bits before
  258|       |      // the shift, which drops bits 16-19 of the code point.
  259|  17.7k|      uint16_t high_surrogate = uint16_t(0xD800 + ((code_point >> 10) & 0x3FF));
  260|  17.7k|      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
  261|  17.7k|      if constexpr (!match_system(big_endian)) {
  262|  17.7k|        high_surrogate = u16_swap_bytes(high_surrogate);
  263|  17.7k|        low_surrogate = u16_swap_bytes(low_surrogate);
  264|  17.7k|      }
  265|  17.7k|      *utf16_output++ = char16_t(high_surrogate);
  266|  17.7k|      *utf16_output++ = char16_t(low_surrogate);
  267|  17.7k|      pos += 4;
  268|  17.7k|    } else {
  269|       |      // we either have too many continuation bytes or an invalid leading byte
  270|    876|      if ((leading_byte & 0b11000000) == 0b10000000) {
  ------------------
  |  Branch (270:11): [True: 378, False: 498]
  ------------------
  271|    378|        return result(error_code::TOO_LONG, pos);
  272|    498|      } else {
  273|    498|        return result(error_code::HEADER_BITS, pos);
  274|    498|      }
  275|    876|    }
  276|  2.06M|  }
  277|    872|  return result(error_code::SUCCESS, utf16_output - start);
  278|  2.58k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf1630rewind_and_convert_with_errorsILNS_10endiannessE0EEENS_6resultEmPKcmPDs:
  298|    864|                                             char16_t *utf16_output) {
  299|    864|  size_t extra_len{0};
  300|       |  // We potentially need to go back in time and find a leading byte.
  301|       |  // In theory '3' would be sufficient, but sometimes the error can go back
  302|       |  // quite far.
  303|    864|  size_t how_far_back = prior_bytes;
  304|       |  // size_t how_far_back = 3; // 3 bytes in the past + current position
  305|       |  // if(how_far_back >= prior_bytes) { how_far_back = prior_bytes; }
  306|    864|  bool found_leading_bytes{false};
  307|       |  // important: it is i <= how_far_back and not 'i < how_far_back'.
  308|    944|  for (size_t i = 0; i <= how_far_back; i++) {
  ------------------
  |  Branch (308:22): [True: 896, False: 48]
  ------------------
  309|    896|    unsigned char byte = buf[-static_cast<std::ptrdiff_t>(i)];
  310|    896|    found_leading_bytes = ((byte & 0b11000000) != 0b10000000);
  311|    896|    if (found_leading_bytes) {
  ------------------
  |  Branch (311:9): [True: 816, False: 80]
  ------------------
  312|    816|      if (i > 0 && byte < 128) {
  ------------------
  |  Branch (312:11): [True: 32, False: 784]
  |  Branch (312:20): [True: 32, False: 0]
  ------------------
  313|       |        // If we had to go back and the leading byte is ascii
  314|       |        // then we can stop right away.
  315|     32|        return result(error_code::TOO_LONG, 0 - i + 1);
  316|     32|      }
  317|    784|      buf -= i;
  318|    784|      extra_len = i;
  319|    784|      break;
  320|    816|    }
  321|    896|  }
  322|       |  //
  323|       |  // It is possible for this function to return a negative count in its result.
  324|       |  // C++ Standard Section 18.1 defines size_t is in <cstddef> which is described
  325|       |  // in C Standard as <stddef.h>. C Standard Section 4.1.5 defines size_t as an
  326|       |  // unsigned integral type of the result of the sizeof operator
  327|       |  //
  328|       |  // An unsigned type will simply wrap round arithmetically (well defined).
  329|       |  //
  330|    832|  if (!found_leading_bytes) {
  ------------------
  |  Branch (330:7): [True: 48, False: 784]
  ------------------
  331|       |    // If how_far_back == 3, we may have four consecutive continuation bytes!!!
  332|       |    // [....] [continuation] [continuation] [continuation] | [buf is
  333|       |    // continuation] Or we possibly have a stream that does not start with a
  334|       |    // leading byte.
  335|     48|    return result(error_code::TOO_LONG, 0 - how_far_back);
  336|     48|  }
  337|    784|  result res = convert_with_errors<endian>(buf, len + extra_len, utf16_output);
  338|    784|  if (res.error) {
  ------------------
  |  Branch (338:7): [True: 656, False: 128]
  ------------------
  339|    656|    res.count -= extra_len;
  340|    656|  }
  341|    784|  return res;
  342|    832|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf1630rewind_and_convert_with_errorsILNS_10endiannessE1EEENS_6resultEmPKcmPDs:
  298|  1.08k|                                             char16_t *utf16_output) {
  299|  1.08k|  size_t extra_len{0};
  300|       |  // We potentially need to go back in time and find a leading byte.
  301|       |  // In theory '3' would be sufficient, but sometimes the error can go back
  302|       |  // quite far.
  303|  1.08k|  size_t how_far_back = prior_bytes;
  304|       |  // size_t how_far_back = 3; // 3 bytes in the past + current position
  305|       |  // if(how_far_back >= prior_bytes) { how_far_back = prior_bytes; }
  306|  1.08k|  bool found_leading_bytes{false};
  307|       |  // important: it is i <= how_far_back and not 'i < how_far_back'.
  308|  1.14k|  for (size_t i = 0; i <= how_far_back; i++) {
  ------------------
  |  Branch (308:22): [True: 1.09k, False: 48]
  ------------------
  309|  1.09k|    unsigned char byte = buf[-static_cast<std::ptrdiff_t>(i)];
  310|  1.09k|    found_leading_bytes = ((byte & 0b11000000) != 0b10000000);
  311|  1.09k|    if (found_leading_bytes) {
  ------------------
  |  Branch (311:9): [True: 1.03k, False: 60]
  ------------------
  312|  1.03k|      if (i > 0 && byte < 128) {
  ------------------
  |  Branch (312:11): [True: 12, False: 1.02k]
  |  Branch (312:20): [True: 12, False: 0]
  ------------------
  313|       |        // If we had to go back and the leading byte is ascii
  314|       |        // then we can stop right away.
  315|     12|        return result(error_code::TOO_LONG, 0 - i + 1);
  316|     12|      }
  317|  1.02k|      buf -= i;
  318|  1.02k|      extra_len = i;
  319|  1.02k|      break;
  320|  1.03k|    }
  321|  1.09k|  }
  322|       |  //
  323|       |  // It is possible for this function to return a negative count in its result.
  324|       |  // C++ Standard Section 18.1 defines size_t is in <cstddef> which is described
  325|       |  // in C Standard as <stddef.h>. C Standard Section 4.1.5 defines size_t as an
  326|       |  // unsigned integral type of the result of the sizeof operator
  327|       |  //
  328|       |  // An unsigned type will simply wrap round arithmetically (well defined).
  329|       |  //
  330|  1.07k|  if (!found_leading_bytes) {
  ------------------
  |  Branch (330:7): [True: 48, False: 1.02k]
  ------------------
  331|       |    // If how_far_back == 3, we may have four consecutive continuation bytes!!!
  332|       |    // [....] [continuation] [continuation] [continuation] | [buf is
  333|       |    // continuation] Or we possibly have a stream that does not start with a
  334|       |    // leading byte.
  335|     48|    return result(error_code::TOO_LONG, 0 - how_far_back);
  336|     48|  }
  337|  1.02k|  result res = convert_with_errors<endian>(buf, len + extra_len, utf16_output);
  338|  1.02k|  if (res.error) {
  ------------------
  |  Branch (338:7): [True: 756, False: 268]
  ------------------
  339|    756|    res.count -= extra_len;
  340|    756|  }
  341|  1.02k|  return res;
  342|  1.07k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf1613convert_validILNS_10endiannessE0EPKcQsr7simdutf6detailE22indexes_into_byte_likeIT0_EEEmS7_mPDs:
   16|  1.14k|                                         char16_t *utf16_output) {
   17|  1.14k|  size_t pos = 0;
   18|  1.14k|  char16_t *start{utf16_output};
   19|  4.47M|  while (pos < len) {
  ------------------
  |  Branch (19:10): [True: 4.47M, False: 1.14k]
  ------------------
   20|       |#if SIMDUTF_CPLUSPLUS23
   21|       |    if !consteval
   22|       |#endif
   23|  4.47M|    {                       // try to convert the next block of 8 ASCII bytes
   24|  4.47M|      if (pos + 8 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (24:11): [True: 4.47M, False: 3.35k]
  ------------------
   25|       |                            // they are ascii
   26|  4.47M|        uint64_t v;
   27|  4.47M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   28|  4.47M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (28:13): [True: 3.21M, False: 1.25M]
  ------------------
   29|  3.21M|          size_t final_pos = pos + 8;
   30|  28.9M|          while (pos < final_pos) {
  ------------------
  |  Branch (30:18): [True: 25.7M, False: 3.21M]
  ------------------
   31|  25.7M|            const char16_t byte = uint8_t(data[pos]);
   32|  25.7M|            *utf16_output++ =
   33|  25.7M|                !match_system(big_endian) ? u16_swap_bytes(byte) : byte;
  ------------------
  |  Branch (33:17): [Folded, False: 25.7M]
  ------------------
   34|  25.7M|            pos++;
   35|  25.7M|          }
   36|  3.21M|          continue;
   37|  3.21M|        }
   38|  4.47M|      }
   39|  4.47M|    }
   40|       |
   41|  1.26M|    auto leading_byte = uint8_t(data[pos]); // leading byte
   42|  1.26M|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (42:9): [True: 872k, False: 390k]
  ------------------
   43|       |      // converting one ASCII byte !!!
   44|   872k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (44:25): [Folded, False: 872k]
  ------------------
   45|   872k|                            ? char16_t(u16_swap_bytes(leading_byte))
   46|   872k|                            : char16_t(leading_byte);
   47|   872k|      pos++;
   48|   872k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (48:16): [True: 211k, False: 178k]
  ------------------
   49|       |      // We have a two-byte UTF-8, it should become
   50|       |      // a single UTF-16 word.
   51|   211k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (51:11): [True: 0, False: 211k]
  ------------------
   52|      0|        break;
   53|      0|      } // minimal bound checking
   54|   211k|      uint16_t code_point = uint16_t(((leading_byte & 0b00011111) << 6) |
   55|   211k|                                     (uint8_t(data[pos + 1]) & 0b00111111));
   56|       |      if constexpr (!match_system(big_endian)) {
   57|       |        code_point = u16_swap_bytes(uint16_t(code_point));
   58|       |      }
   59|   211k|      *utf16_output++ = char16_t(code_point);
   60|   211k|      pos += 2;
   61|   211k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (61:16): [True: 167k, False: 10.9k]
  ------------------
   62|       |      // We have a three-byte UTF-8, it should become
   63|       |      // a single UTF-16 word.
   64|   167k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (64:11): [True: 0, False: 167k]
  ------------------
   65|      0|        break;
   66|      0|      } // minimal bound checking
   67|   167k|      uint16_t code_point =
   68|   167k|          uint16_t(((leading_byte & 0b00001111) << 12) |
   69|   167k|                   ((uint8_t(data[pos + 1]) & 0b00111111) << 6) |
   70|   167k|                   (uint8_t(data[pos + 2]) & 0b00111111));
   71|       |      if constexpr (!match_system(big_endian)) {
   72|       |        code_point = u16_swap_bytes(uint16_t(code_point));
   73|       |      }
   74|   167k|      *utf16_output++ = char16_t(code_point);
   75|   167k|      pos += 3;
   76|   167k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (76:16): [True: 10.9k, False: 0]
  ------------------
   77|       |      // we have a 4-byte UTF-8 word.
   78|  10.9k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (78:11): [True: 0, False: 10.9k]
  ------------------
   79|      0|        break;
   80|      0|      } // minimal bound checking
   81|  10.9k|      uint32_t code_point = ((leading_byte & 0b00000111) << 18) |
   82|  10.9k|                            ((uint8_t(data[pos + 1]) & 0b00111111) << 12) |
   83|  10.9k|                            ((uint8_t(data[pos + 2]) & 0b00111111) << 6) |
   84|  10.9k|                            (uint8_t(data[pos + 3]) & 0b00111111);
   85|  10.9k|      code_point -= 0x10000;
   86|       |      // The mask is a no-op (code_point < 0x100000). It stops clang 18's SLP
   87|       |      // vectorizer on RISC-V V from narrowing code_point to 16 bits before
   88|       |      // the shift, which drops bits 16-19 of the code point.
   89|  10.9k|      uint16_t high_surrogate = uint16_t(0xD800 + ((code_point >> 10) & 0x3FF));
   90|  10.9k|      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
   91|       |      if constexpr (!match_system(big_endian)) {
   92|       |        high_surrogate = u16_swap_bytes(high_surrogate);
   93|       |        low_surrogate = u16_swap_bytes(low_surrogate);
   94|       |      }
   95|  10.9k|      *utf16_output++ = char16_t(high_surrogate);
   96|  10.9k|      *utf16_output++ = char16_t(low_surrogate);
   97|  10.9k|      pos += 4;
   98|  10.9k|    } else {
   99|       |      // we may have a continuation but we do not do error checking
  100|      0|      return 0;
  101|      0|    }
  102|  1.26M|  }
  103|  1.14k|  return utf16_output - start;
  104|  1.14k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf1613convert_validILNS_10endiannessE1EPKcQsr7simdutf6detailE22indexes_into_byte_likeIT0_EEEmS7_mPDs:
   16|  1.55k|                                         char16_t *utf16_output) {
   17|  1.55k|  size_t pos = 0;
   18|  1.55k|  char16_t *start{utf16_output};
   19|  4.21M|  while (pos < len) {
  ------------------
  |  Branch (19:10): [True: 4.21M, False: 1.55k]
  ------------------
   20|       |#if SIMDUTF_CPLUSPLUS23
   21|       |    if !consteval
   22|       |#endif
   23|  4.21M|    {                       // try to convert the next block of 8 ASCII bytes
   24|  4.21M|      if (pos + 8 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (24:11): [True: 4.21M, False: 4.94k]
  ------------------
   25|       |                            // they are ascii
   26|  4.21M|        uint64_t v;
   27|  4.21M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   28|  4.21M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (28:13): [True: 3.40M, False: 801k]
  ------------------
   29|  3.40M|          size_t final_pos = pos + 8;
   30|  30.6M|          while (pos < final_pos) {
  ------------------
  |  Branch (30:18): [True: 27.2M, False: 3.40M]
  ------------------
   31|  27.2M|            const char16_t byte = uint8_t(data[pos]);
   32|  27.2M|            *utf16_output++ =
   33|  27.2M|                !match_system(big_endian) ? u16_swap_bytes(byte) : byte;
  ------------------
  |  Branch (33:17): [True: 27.2M, Folded]
  ------------------
   34|  27.2M|            pos++;
   35|  27.2M|          }
   36|  3.40M|          continue;
   37|  3.40M|        }
   38|  4.21M|      }
   39|  4.21M|    }
   40|       |
   41|   806k|    auto leading_byte = uint8_t(data[pos]); // leading byte
   42|   806k|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (42:9): [True: 522k, False: 283k]
  ------------------
   43|       |      // converting one ASCII byte !!!
   44|   522k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (44:25): [True: 522k, Folded]
  ------------------
   45|   522k|                            ? char16_t(u16_swap_bytes(leading_byte))
   46|   522k|                            : char16_t(leading_byte);
   47|   522k|      pos++;
   48|   522k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (48:16): [True: 194k, False: 89.3k]
  ------------------
   49|       |      // We have a two-byte UTF-8, it should become
   50|       |      // a single UTF-16 word.
   51|   194k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (51:11): [True: 0, False: 194k]
  ------------------
   52|      0|        break;
   53|      0|      } // minimal bound checking
   54|   194k|      uint16_t code_point = uint16_t(((leading_byte & 0b00011111) << 6) |
   55|   194k|                                     (uint8_t(data[pos + 1]) & 0b00111111));
   56|   194k|      if constexpr (!match_system(big_endian)) {
   57|   194k|        code_point = u16_swap_bytes(uint16_t(code_point));
   58|   194k|      }
   59|   194k|      *utf16_output++ = char16_t(code_point);
   60|   194k|      pos += 2;
   61|   194k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (61:16): [True: 62.7k, False: 26.5k]
  ------------------
   62|       |      // We have a three-byte UTF-8, it should become
   63|       |      // a single UTF-16 word.
   64|  62.7k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (64:11): [True: 0, False: 62.7k]
  ------------------
   65|      0|        break;
   66|      0|      } // minimal bound checking
   67|  62.7k|      uint16_t code_point =
   68|  62.7k|          uint16_t(((leading_byte & 0b00001111) << 12) |
   69|  62.7k|                   ((uint8_t(data[pos + 1]) & 0b00111111) << 6) |
   70|  62.7k|                   (uint8_t(data[pos + 2]) & 0b00111111));
   71|  62.7k|      if constexpr (!match_system(big_endian)) {
   72|  62.7k|        code_point = u16_swap_bytes(uint16_t(code_point));
   73|  62.7k|      }
   74|  62.7k|      *utf16_output++ = char16_t(code_point);
   75|  62.7k|      pos += 3;
   76|  62.7k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (76:16): [True: 26.5k, False: 0]
  ------------------
   77|       |      // we have a 4-byte UTF-8 word.
   78|  26.5k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (78:11): [True: 0, False: 26.5k]
  ------------------
   79|      0|        break;
   80|      0|      } // minimal bound checking
   81|  26.5k|      uint32_t code_point = ((leading_byte & 0b00000111) << 18) |
   82|  26.5k|                            ((uint8_t(data[pos + 1]) & 0b00111111) << 12) |
   83|  26.5k|                            ((uint8_t(data[pos + 2]) & 0b00111111) << 6) |
   84|  26.5k|                            (uint8_t(data[pos + 3]) & 0b00111111);
   85|  26.5k|      code_point -= 0x10000;
   86|       |      // The mask is a no-op (code_point < 0x100000). It stops clang 18's SLP
   87|       |      // vectorizer on RISC-V V from narrowing code_point to 16 bits before
   88|       |      // the shift, which drops bits 16-19 of the code point.
   89|  26.5k|      uint16_t high_surrogate = uint16_t(0xD800 + ((code_point >> 10) & 0x3FF));
   90|  26.5k|      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
   91|  26.5k|      if constexpr (!match_system(big_endian)) {
   92|  26.5k|        high_surrogate = u16_swap_bytes(high_surrogate);
   93|  26.5k|        low_surrogate = u16_swap_bytes(low_surrogate);
   94|  26.5k|      }
   95|  26.5k|      *utf16_output++ = char16_t(high_surrogate);
   96|  26.5k|      *utf16_output++ = char16_t(low_surrogate);
   97|  26.5k|      pos += 4;
   98|  26.5k|    } else {
   99|       |      // we may have a continuation but we do not do error checking
  100|      0|      return 0;
  101|      0|    }
  102|   806k|  }
  103|  1.55k|  return utf16_output - start;
  104|  1.55k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf3230rewind_and_convert_with_errorsEmPKcmPDi:
  249|  1.02k|                                             char32_t *utf32_output) {
  250|  1.02k|  size_t extra_len{0};
  251|       |  // We potentially need to go back in time and find a leading byte.
  252|  1.02k|  size_t how_far_back = 3; // 3 bytes in the past + current position
  253|  1.02k|  if (how_far_back > prior_bytes) {
  ------------------
  |  Branch (253:7): [True: 272, False: 756]
  ------------------
  254|    272|    how_far_back = prior_bytes;
  255|    272|  }
  256|  1.02k|  bool found_leading_bytes{false};
  257|       |  // important: it is i <= how_far_back and not 'i < how_far_back'.
  258|  1.10k|  for (size_t i = 0; i <= how_far_back; i++) {
  ------------------
  |  Branch (258:22): [True: 1.05k, False: 44]
  ------------------
  259|  1.05k|    unsigned char byte = buf[-static_cast<std::ptrdiff_t>(i)];
  260|  1.05k|    found_leading_bytes = ((byte & 0b11000000) != 0b10000000);
  261|  1.05k|    if (found_leading_bytes) {
  ------------------
  |  Branch (261:9): [True: 984, False: 72]
  ------------------
  262|    984|      if (i > 0 && byte < 128) {
  ------------------
  |  Branch (262:11): [True: 28, False: 956]
  |  Branch (262:20): [True: 28, False: 0]
  ------------------
  263|       |        // If we had to go back and the leading byte is ascii
  264|       |        // then we can stop right away.
  265|     28|        return result(error_code::TOO_LONG, 0 - i + 1);
  266|     28|      }
  267|    956|      buf -= i;
  268|    956|      extra_len = i;
  269|    956|      break;
  270|    984|    }
  271|  1.05k|  }
  272|       |  //
  273|       |  // It is possible for this function to return a negative count in its result.
  274|       |  // C++ Standard Section 18.1 defines size_t is in <cstddef> which is described
  275|       |  // in C Standard as <stddef.h>. C Standard Section 4.1.5 defines size_t as an
  276|       |  // unsigned integral type of the result of the sizeof operator
  277|       |  //
  278|       |  // An unsigned type will simply wrap round arithmetically (well defined).
  279|       |  //
  280|  1.00k|  if (!found_leading_bytes) {
  ------------------
  |  Branch (280:7): [True: 44, False: 956]
  ------------------
  281|       |    // If how_far_back == 3, we may have four consecutive continuation bytes!!!
  282|       |    // [....] [continuation] [continuation] [continuation] | [buf is
  283|       |    // continuation] Or we possibly have a stream that does not start with a
  284|       |    // leading byte.
  285|     44|    return result(error_code::TOO_LONG, 0 - how_far_back);
  286|     44|  }
  287|       |
  288|    956|  result res = convert_with_errors(buf, len + extra_len, utf32_output);
  289|    956|  if (res.error) {
  ------------------
  |  Branch (289:7): [True: 700, False: 256]
  ------------------
  290|    700|    res.count -= extra_len;
  291|    700|  }
  292|    956|  return res;
  293|  1.00k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf3219convert_with_errorsIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEENS_6resultES6_mPDi:
  121|  2.50k|                                               char32_t *utf32_output) {
  122|  2.50k|  size_t pos = 0;
  123|  2.50k|  char32_t *start{utf32_output};
  124|  3.26M|  while (pos < len) {
  ------------------
  |  Branch (124:10): [True: 3.26M, False: 886]
  ------------------
  125|       |#if SIMDUTF_CPLUSPLUS23
  126|       |    if !consteval
  127|       |#endif
  128|  3.26M|    {
  129|       |      // try to convert the next block of 16 ASCII bytes
  130|  3.26M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (130:11): [True: 3.25M, False: 9.19k]
  ------------------
  131|       |                             // they are ascii
  132|  3.25M|        uint64_t v1;
  133|  3.25M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
  134|  3.25M|        uint64_t v2;
  135|  3.25M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  136|  3.25M|        uint64_t v{v1 | v2};
  137|  3.25M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (137:13): [True: 2.14M, False: 1.10M]
  ------------------
  138|  2.14M|          size_t final_pos = pos + 16;
  139|  36.4M|          while (pos < final_pos) {
  ------------------
  |  Branch (139:18): [True: 34.3M, False: 2.14M]
  ------------------
  140|  34.3M|            *utf32_output++ = uint8_t(data[pos]);
  141|  34.3M|            pos++;
  142|  34.3M|          }
  143|  2.14M|          continue;
  144|  2.14M|        }
  145|  3.25M|      }
  146|  3.26M|    }
  147|  1.11M|    auto leading_byte = uint8_t(data[pos]); // leading byte
  148|  1.11M|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (148:9): [True: 669k, False: 447k]
  ------------------
  149|       |      // converting one ASCII byte !!!
  150|   669k|      *utf32_output++ = char32_t(leading_byte);
  151|   669k|      pos++;
  152|   669k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (152:16): [True: 170k, False: 277k]
  ------------------
  153|       |      // We have a two-byte UTF-8
  154|   170k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (154:11): [True: 102, False: 169k]
  ------------------
  155|    102|        return result(error_code::TOO_SHORT, pos);
  156|    102|      } // minimal bound checking
  157|   169k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (157:11): [True: 306, False: 169k]
  ------------------
  158|    306|        return result(error_code::TOO_SHORT, pos);
  159|    306|      }
  160|       |      // range check
  161|   169k|      uint32_t code_point = (leading_byte & 0b00011111) << 6 |
  162|   169k|                            (uint8_t(data[pos + 1]) & 0b00111111);
  163|   169k|      if (code_point < 0x80) {
  ------------------
  |  Branch (163:11): [True: 18, False: 169k]
  ------------------
  164|     18|        return result(error_code::OVERLONG, pos);
  165|     18|      }
  166|   169k|      *utf32_output++ = char32_t(code_point);
  167|   169k|      pos += 2;
  168|   277k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (168:16): [True: 262k, False: 14.9k]
  ------------------
  169|       |      // We have a three-byte UTF-8
  170|   262k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (170:11): [True: 90, False: 262k]
  ------------------
  171|     90|        return result(error_code::TOO_SHORT, pos);
  172|     90|      } // minimal bound checking
  173|       |
  174|   262k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (174:11): [True: 120, False: 262k]
  ------------------
  175|    120|        return result(error_code::TOO_SHORT, pos);
  176|    120|      }
  177|   262k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (177:11): [True: 60, False: 262k]
  ------------------
  178|     60|        return result(error_code::TOO_SHORT, pos);
  179|     60|      }
  180|       |      // range check
  181|   262k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
  182|   262k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 6 |
  183|   262k|                            (uint8_t(data[pos + 2]) & 0b00111111);
  184|   262k|      if (code_point < 0x800) {
  ------------------
  |  Branch (184:11): [True: 42, False: 262k]
  ------------------
  185|     42|        return result(error_code::OVERLONG, pos);
  186|     42|      }
  187|   262k|      if (0xd7ff < code_point && code_point < 0xe000) {
  ------------------
  |  Branch (187:11): [True: 214k, False: 47.5k]
  |  Branch (187:34): [True: 6, False: 214k]
  ------------------
  188|      6|        return result(error_code::SURROGATE, pos);
  189|      6|      }
  190|   262k|      *utf32_output++ = char32_t(code_point);
  191|   262k|      pos += 3;
  192|   262k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (192:16): [True: 14.3k, False: 648]
  ------------------
  193|       |      // we have a 4-byte UTF-8 word.
  194|  14.3k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (194:11): [True: 30, False: 14.2k]
  ------------------
  195|     30|        return result(error_code::TOO_SHORT, pos);
  196|     30|      } // minimal bound checking
  197|  14.2k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (197:11): [True: 72, False: 14.2k]
  ------------------
  198|     72|        return result(error_code::TOO_SHORT, pos);
  199|     72|      }
  200|  14.2k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (200:11): [True: 42, False: 14.1k]
  ------------------
  201|     42|        return result(error_code::TOO_SHORT, pos);
  202|     42|      }
  203|  14.1k|      if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (203:11): [True: 18, False: 14.1k]
  ------------------
  204|     18|        return result(error_code::TOO_SHORT, pos);
  205|     18|      }
  206|       |
  207|       |      // range check
  208|  14.1k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  209|  14.1k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 12 |
  210|  14.1k|                            (uint8_t(data[pos + 2]) & 0b00111111) << 6 |
  211|  14.1k|                            (uint8_t(data[pos + 3]) & 0b00111111);
  212|  14.1k|      if (code_point <= 0xffff) {
  ------------------
  |  Branch (212:11): [True: 30, False: 14.1k]
  ------------------
  213|     30|        return result(error_code::OVERLONG, pos);
  214|     30|      }
  215|  14.1k|      if (0x10ffff < code_point) {
  ------------------
  |  Branch (215:11): [True: 36, False: 14.0k]
  ------------------
  216|     36|        return result(error_code::TOO_LARGE, pos);
  217|     36|      }
  218|  14.0k|      *utf32_output++ = char32_t(code_point);
  219|  14.0k|      pos += 4;
  220|  14.0k|    } else {
  221|       |      // we either have too many continuation bytes or an invalid leading byte
  222|    648|      if ((leading_byte & 0b11000000) == 0b10000000) {
  ------------------
  |  Branch (222:11): [True: 378, False: 270]
  ------------------
  223|    378|        return result(error_code::TOO_LONG, pos);
  224|    378|      } else {
  225|    270|        return result(error_code::HEADER_BITS, pos);
  226|    270|      }
  227|    648|    }
  228|  1.11M|  }
  229|    886|  return result(error_code::SUCCESS, utf32_output - start);
  230|  2.50k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf327convertIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEEmS6_mPDi:
   16|  2.04k|                                   char32_t *utf32_output) {
   17|  2.04k|  size_t pos = 0;
   18|  2.04k|  char32_t *start{utf32_output};
   19|  2.69M|  while (pos < len) {
  ------------------
  |  Branch (19:10): [True: 2.69M, False: 690]
  ------------------
   20|       |#if SIMDUTF_CPLUSPLUS23
   21|       |    if !consteval
   22|       |#endif
   23|  2.69M|    {
   24|       |      // try to convert the next block of 16 ASCII bytes
   25|  2.69M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (25:11): [True: 2.68M, False: 5.18k]
  ------------------
   26|       |                             // they are ascii
   27|  2.68M|        uint64_t v1;
   28|  2.68M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
   29|  2.68M|        uint64_t v2;
   30|  2.68M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   31|  2.68M|        uint64_t v{v1 | v2};
   32|  2.68M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (32:13): [True: 1.39M, False: 1.29M]
  ------------------
   33|  1.39M|          size_t final_pos = pos + 16;
   34|  23.7M|          while (pos < final_pos) {
  ------------------
  |  Branch (34:18): [True: 22.3M, False: 1.39M]
  ------------------
   35|  22.3M|            *utf32_output++ = uint8_t(data[pos]);
   36|  22.3M|            pos++;
   37|  22.3M|          }
   38|  1.39M|          continue;
   39|  1.39M|        }
   40|  2.68M|      }
   41|  2.69M|    }
   42|  1.29M|    auto leading_byte = uint8_t(data[pos]); // leading byte
   43|  1.29M|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (43:9): [True: 1.06M, False: 229k]
  ------------------
   44|       |      // converting one ASCII byte !!!
   45|  1.06M|      *utf32_output++ = char32_t(leading_byte);
   46|  1.06M|      pos++;
   47|  1.06M|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (47:16): [True: 137k, False: 92.1k]
  ------------------
   48|       |      // We have a two-byte UTF-8
   49|   137k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (49:11): [True: 78, False: 136k]
  ------------------
   50|     78|        return 0;
   51|     78|      } // minimal bound checking
   52|   136k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (52:11): [True: 212, False: 136k]
  ------------------
   53|    212|        return 0;
   54|    212|      }
   55|       |      // range check
   56|   136k|      uint32_t code_point = (leading_byte & 0b00011111) << 6 |
   57|   136k|                            (uint8_t(data[pos + 1]) & 0b00111111);
   58|   136k|      if (code_point < 0x80) {
  ------------------
  |  Branch (58:11): [True: 34, False: 136k]
  ------------------
   59|     34|        return 0;
   60|     34|      }
   61|   136k|      *utf32_output++ = char32_t(code_point);
   62|   136k|      pos += 2;
   63|   136k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (63:16): [True: 83.4k, False: 8.66k]
  ------------------
   64|       |      // We have a three-byte UTF-8
   65|  83.4k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (65:11): [True: 54, False: 83.3k]
  ------------------
   66|     54|        return 0;
   67|     54|      } // minimal bound checking
   68|       |
   69|  83.3k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (69:11): [True: 118, False: 83.2k]
  ------------------
   70|    118|        return 0;
   71|    118|      }
   72|  83.2k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (72:11): [True: 48, False: 83.2k]
  ------------------
   73|     48|        return 0;
   74|     48|      }
   75|       |      // range check
   76|  83.2k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
   77|  83.2k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 6 |
   78|  83.2k|                            (uint8_t(data[pos + 2]) & 0b00111111);
   79|  83.2k|      if (code_point < 0x800 || (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (79:11): [True: 32, False: 83.1k]
  |  Branch (79:34): [True: 928, False: 82.2k]
  |  Branch (79:57): [True: 16, False: 912]
  ------------------
   80|     48|        return 0;
   81|     48|      }
   82|  83.1k|      *utf32_output++ = char32_t(code_point);
   83|  83.1k|      pos += 3;
   84|  83.1k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (84:16): [True: 8.13k, False: 522]
  ------------------
   85|       |      // we have a 4-byte UTF-8 word.
   86|  8.13k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (86:11): [True: 18, False: 8.12k]
  ------------------
   87|     18|        return 0;
   88|     18|      } // minimal bound checking
   89|  8.12k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (89:11): [True: 82, False: 8.03k]
  ------------------
   90|     82|        return 0;
   91|     82|      }
   92|  8.03k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (92:11): [True: 32, False: 8.00k]
  ------------------
   93|     32|        return 0;
   94|     32|      }
   95|  8.00k|      if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (95:11): [True: 26, False: 7.98k]
  ------------------
   96|     26|        return 0;
   97|     26|      }
   98|       |
   99|       |      // range check
  100|  7.98k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  101|  7.98k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 12 |
  102|  7.98k|                            (uint8_t(data[pos + 2]) & 0b00111111) << 6 |
  103|  7.98k|                            (uint8_t(data[pos + 3]) & 0b00111111);
  104|  7.98k|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (104:11): [True: 38, False: 7.94k]
  |  Branch (104:35): [True: 44, False: 7.89k]
  ------------------
  105|     82|        return 0;
  106|     82|      }
  107|  7.89k|      *utf32_output++ = char32_t(code_point);
  108|  7.89k|      pos += 4;
  109|  7.89k|    } else {
  110|    522|      return 0;
  111|    522|    }
  112|  1.29M|  }
  113|    690|  return utf32_output - start;
  114|  2.04k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf3213convert_validIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEEmS6_mPDi:
   16|  1.51k|                                         char32_t *utf32_output) {
   17|  1.51k|  size_t pos = 0;
   18|  1.51k|  char32_t *start{utf32_output};
   19|  3.58M|  while (pos < len) {
  ------------------
  |  Branch (19:10): [True: 3.58M, False: 1.51k]
  ------------------
   20|       |#if SIMDUTF_CPLUSPLUS23
   21|       |    if !consteval
   22|       |#endif
   23|  3.58M|    {
   24|       |      // try to convert the next block of 8 ASCII bytes
   25|  3.58M|      if (pos + 8 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (25:11): [True: 3.58M, False: 4.63k]
  ------------------
   26|       |                            // they are ascii
   27|  3.58M|        uint64_t v;
   28|  3.58M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   29|  3.58M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (29:13): [True: 3.22M, False: 360k]
  ------------------
   30|  3.22M|          size_t final_pos = pos + 8;
   31|  29.0M|          while (pos < final_pos) {
  ------------------
  |  Branch (31:18): [True: 25.7M, False: 3.22M]
  ------------------
   32|  25.7M|            *utf32_output++ = uint8_t(data[pos]);
   33|  25.7M|            pos++;
   34|  25.7M|          }
   35|  3.22M|          continue;
   36|  3.22M|        }
   37|  3.58M|      }
   38|  3.58M|    }
   39|   364k|    auto leading_byte = uint8_t(data[pos]); // leading byte
   40|   364k|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (40:9): [True: 204k, False: 160k]
  ------------------
   41|       |      // converting one ASCII byte !!!
   42|   204k|      *utf32_output++ = char32_t(leading_byte);
   43|   204k|      pos++;
   44|   204k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (44:16): [True: 118k, False: 41.4k]
  ------------------
   45|       |      // We have a two-byte UTF-8
   46|   118k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (46:11): [True: 0, False: 118k]
  ------------------
   47|      0|        break;
   48|      0|      } // minimal bound checking
   49|   118k|      *utf32_output++ = char32_t(((leading_byte & 0b00011111) << 6) |
   50|   118k|                                 (uint8_t(data[pos + 1]) & 0b00111111));
   51|   118k|      pos += 2;
   52|   118k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (52:16): [True: 24.1k, False: 17.3k]
  ------------------
   53|       |      // We have a three-byte UTF-8
   54|  24.1k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (54:11): [True: 0, False: 24.1k]
  ------------------
   55|      0|        break;
   56|      0|      } // minimal bound checking
   57|  24.1k|      *utf32_output++ = char32_t(((leading_byte & 0b00001111) << 12) |
   58|  24.1k|                                 ((uint8_t(data[pos + 1]) & 0b00111111) << 6) |
   59|  24.1k|                                 (uint8_t(data[pos + 2]) & 0b00111111));
   60|  24.1k|      pos += 3;
   61|  24.1k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (61:16): [True: 17.3k, False: 0]
  ------------------
   62|       |      // we have a 4-byte UTF-8 word.
   63|  17.3k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (63:11): [True: 0, False: 17.3k]
  ------------------
   64|      0|        break;
   65|      0|      } // minimal bound checking
   66|  17.3k|      uint32_t code_word = ((leading_byte & 0b00000111) << 18) |
   67|  17.3k|                           ((uint8_t(data[pos + 1]) & 0b00111111) << 12) |
   68|  17.3k|                           ((uint8_t(data[pos + 2]) & 0b00111111) << 6) |
   69|  17.3k|                           (uint8_t(data[pos + 3]) & 0b00111111);
   70|  17.3k|      *utf32_output++ = char32_t(code_word);
   71|  17.3k|      pos += 4;
   72|  17.3k|    } else {
   73|       |      // we may have a continuation but we do not do error checking
   74|      0|      return 0;
   75|      0|    }
   76|   364k|  }
   77|  1.51k|  return utf32_output - start;
   78|  1.51k|}

_ZNK7simdutf8fallback14implementation13validate_utf8EPKcm:
   37|  4.20k|implementation::validate_utf8(const char *buf, size_t len) const noexcept {
   38|  4.20k|  return scalar::utf8::validate(buf, len);
   39|  4.20k|}
_ZNK7simdutf8fallback14implementation25validate_utf8_with_errorsEPKcm:
   44|  4.20k|    const char *buf, size_t len) const noexcept {
   45|  4.20k|  return scalar::utf8::validate_with_errors(buf, len);
   46|  4.20k|}
_ZNK7simdutf8fallback14implementation16validate_utf16leEPKDsm:
   77|  1.43k|                                 size_t len) const noexcept {
   78|  1.43k|  return scalar::utf16::validate<endianness::LITTLE>(buf, len);
   79|  1.43k|}
_ZNK7simdutf8fallback14implementation16validate_utf16beEPKDsm:
   85|  1.32k|                                 size_t len) const noexcept {
   86|  1.32k|  return scalar::utf16::validate<endianness::BIG>(buf, len);
   87|  1.32k|}
_ZNK7simdutf8fallback14implementation28validate_utf16le_with_errorsEPKDsm:
   90|  1.43k|    const char16_t *buf, size_t len) const noexcept {
   91|  1.43k|  return scalar::utf16::validate_with_errors<endianness::LITTLE>(buf, len);
   92|  1.43k|}
_ZNK7simdutf8fallback14implementation28validate_utf16be_with_errorsEPKDsm:
   95|  1.32k|    const char16_t *buf, size_t len) const noexcept {
   96|  1.32k|  return scalar::utf16::validate_with_errors<endianness::BIG>(buf, len);
   97|  1.32k|}
_ZNK7simdutf8fallback14implementation14validate_utf32EPKDim:
  114|  2.64k|implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept {
  115|  2.64k|  return scalar::utf32::validate(buf, len);
  116|  2.64k|}
_ZNK7simdutf8fallback14implementation26validate_utf32_with_errorsEPKDim:
  121|  2.64k|    const char32_t *buf, size_t len) const noexcept {
  122|  2.64k|  return scalar::utf32::validate_with_errors(buf, len);
  123|  2.64k|}
_ZNK7simdutf8fallback14implementation22convert_latin1_to_utf8EPKcmPc:
  128|    560|    const char *buf, size_t len, char *utf8_output) const noexcept {
  129|    560|  return scalar::latin1_to_utf8::convert(buf, len, utf8_output);
  130|    560|}
_ZNK7simdutf8fallback14implementation25convert_latin1_to_utf16leEPKcmPDs:
  135|     76|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  136|     76|  return scalar::latin1_to_utf16::convert<endianness::LITTLE>(buf, len,
  137|     76|                                                              utf16_output);
  138|     76|}
_ZNK7simdutf8fallback14implementation25convert_latin1_to_utf16beEPKcmPDs:
  141|    110|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  142|    110|  return scalar::latin1_to_utf16::convert<endianness::BIG>(buf, len,
  143|    110|                                                           utf16_output);
  144|    110|}
_ZNK7simdutf8fallback14implementation23convert_latin1_to_utf32EPKcmPDi:
  149|     96|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  150|     96|  return scalar::latin1_to_utf32::convert(buf, len, utf32_output);
  151|     96|}
_ZNK7simdutf8fallback14implementation22convert_utf8_to_latin1EPKcmPc:
  156|    710|    const char *buf, size_t len, char *latin1_output) const noexcept {
  157|    710|  return scalar::utf8_to_latin1::convert(buf, len, latin1_output);
  158|    710|}
_ZNK7simdutf8fallback14implementation34convert_utf8_to_latin1_with_errorsEPKcmPc:
  161|    730|    const char *buf, size_t len, char *latin1_output) const noexcept {
  162|    730|  return scalar::utf8_to_latin1::convert_with_errors(buf, len, latin1_output);
  163|    730|}
_ZNK7simdutf8fallback14implementation23convert_utf8_to_utf16leEPKcmPDs:
  173|    950|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  174|    950|  return scalar::utf8_to_utf16::convert<endianness::LITTLE>(buf, len,
  175|    950|                                                            utf16_output);
  176|    950|}
_ZNK7simdutf8fallback14implementation23convert_utf8_to_utf16beEPKcmPDs:
  179|  1.00k|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  180|  1.00k|  return scalar::utf8_to_utf16::convert<endianness::BIG>(buf, len,
  181|  1.00k|                                                         utf16_output);
  182|  1.00k|}
_ZNK7simdutf8fallback14implementation35convert_utf8_to_utf16le_with_errorsEPKcmPDs:
  185|    746|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  186|    746|  return scalar::utf8_to_utf16::convert_with_errors<endianness::LITTLE>(
  187|    746|      buf, len, utf16_output);
  188|    746|}
_ZNK7simdutf8fallback14implementation35convert_utf8_to_utf16be_with_errorsEPKcmPDs:
  191|    934|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  192|    934|  return scalar::utf8_to_utf16::convert_with_errors<endianness::BIG>(
  193|    934|      buf, len, utf16_output);
  194|    934|}
_ZNK7simdutf8fallback14implementation29convert_valid_utf8_to_utf16leEPKcmPDs:
  197|    380|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  198|    380|  return scalar::utf8_to_utf16::convert_valid<endianness::LITTLE>(buf, len,
  199|    380|                                                                  utf16_output);
  200|    380|}
_ZNK7simdutf8fallback14implementation29convert_valid_utf8_to_utf16beEPKcmPDs:
  203|    518|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  204|    518|  return scalar::utf8_to_utf16::convert_valid<endianness::BIG>(buf, len,
  205|    518|                                                               utf16_output);
  206|    518|}
_ZNK7simdutf8fallback14implementation21convert_utf8_to_utf32EPKcmPDi:
  211|    956|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  212|    956|  return scalar::utf8_to_utf32::convert(buf, len, utf32_output);
  213|    956|}
_ZNK7simdutf8fallback14implementation33convert_utf8_to_utf32_with_errorsEPKcmPDi:
  216|    926|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  217|    926|  return scalar::utf8_to_utf32::convert_with_errors(buf, len, utf32_output);
  218|    926|}
_ZNK7simdutf8fallback14implementation27convert_valid_utf8_to_utf32EPKcmPDi:
  221|    506|    const char *input, size_t size, char32_t *utf32_output) const noexcept {
  222|    506|  return scalar::utf8_to_utf32::convert_valid(input, size, utf32_output);
  223|    506|}
_ZNK7simdutf8fallback14implementation25convert_utf16le_to_latin1EPKDsmPc:
  228|    120|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  229|    120|  return scalar::utf16_to_latin1::convert<endianness::LITTLE>(buf, len,
  230|    120|                                                              latin1_output);
  231|    120|}
_ZNK7simdutf8fallback14implementation25convert_utf16be_to_latin1EPKDsmPc:
  234|     98|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  235|     98|  return scalar::utf16_to_latin1::convert<endianness::BIG>(buf, len,
  236|     98|                                                           latin1_output);
  237|     98|}
_ZNK7simdutf8fallback14implementation37convert_utf16le_to_latin1_with_errorsEPKDsmPc:
  241|    280|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  242|    280|  return scalar::utf16_to_latin1::convert_with_errors<endianness::LITTLE>(
  243|    280|      buf, len, latin1_output);
  244|    280|}
_ZNK7simdutf8fallback14implementation37convert_utf16be_to_latin1_with_errorsEPKDsmPc:
  248|    248|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  249|    248|  return scalar::utf16_to_latin1::convert_with_errors<endianness::BIG>(
  250|    248|      buf, len, latin1_output);
  251|    248|}
_ZNK7simdutf8fallback14implementation23convert_utf16le_to_utf8EPKDsmPc:
  268|    472|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  269|    472|  return scalar::utf16_to_utf8::convert<endianness::LITTLE>(buf, len,
  270|    472|                                                            utf8_output);
  271|    472|}
_ZNK7simdutf8fallback14implementation23convert_utf16be_to_utf8EPKDsmPc:
  274|    396|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  275|    396|  return scalar::utf16_to_utf8::convert<endianness::BIG>(buf, len, utf8_output);
  276|    396|}
_ZNK7simdutf8fallback14implementation35convert_utf16le_to_utf8_with_errorsEPKDsmPc:
  279|    686|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  280|    686|  return scalar::utf16_to_utf8::convert_with_errors<endianness::LITTLE>(
  281|    686|      buf, len, utf8_output);
  282|    686|}
_ZNK7simdutf8fallback14implementation35convert_utf16be_to_utf8_with_errorsEPKDsmPc:
  285|    556|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  286|    556|  return scalar::utf16_to_utf8::convert_with_errors<endianness::BIG>(
  287|    556|      buf, len, utf8_output);
  288|    556|}
_ZNK7simdutf8fallback14implementation29convert_valid_utf16le_to_utf8EPKDsmPc:
  291|    402|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  292|    402|  return scalar::utf16_to_utf8::convert_valid<endianness::LITTLE>(buf, len,
  293|    402|                                                                  utf8_output);
  294|    402|}
_ZNK7simdutf8fallback14implementation29convert_valid_utf16be_to_utf8EPKDsmPc:
  297|    450|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  298|    450|  return scalar::utf16_to_utf8::convert_valid<endianness::BIG>(buf, len,
  299|    450|                                                               utf8_output);
  300|    450|}
_ZNK7simdutf8fallback14implementation23convert_utf32_to_latin1EPKDimPc:
  305|    186|    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
  306|    186|  return scalar::utf32_to_latin1::convert(buf, len, latin1_output);
  307|    186|}
_ZNK7simdutf8fallback14implementation35convert_utf32_to_latin1_with_errorsEPKDimPc:
  310|    420|    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
  311|    420|  return scalar::utf32_to_latin1::convert_with_errors(buf, len, latin1_output);
  312|    420|}
_ZNK7simdutf8fallback14implementation21convert_utf32_to_utf8EPKDimPc:
  322|    726|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  323|    726|  return scalar::utf32_to_utf8::convert(buf, len, utf8_output);
  324|    726|}
_ZNK7simdutf8fallback14implementation33convert_utf32_to_utf8_with_errorsEPKDimPc:
  327|    864|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  328|    864|  return scalar::utf32_to_utf8::convert_with_errors(buf, len, utf8_output);
  329|    864|}
_ZNK7simdutf8fallback14implementation27convert_valid_utf32_to_utf8EPKDimPc:
  332|    216|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  333|    216|  return scalar::utf32_to_utf8::convert_valid(buf, len, utf8_output);
  334|    216|}
_ZNK7simdutf8fallback14implementation24convert_utf32_to_utf16leEPKDimPDs:
  339|    486|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  340|    486|  return scalar::utf32_to_utf16::convert<endianness::LITTLE>(buf, len,
  341|    486|                                                             utf16_output);
  342|    486|}
_ZNK7simdutf8fallback14implementation24convert_utf32_to_utf16beEPKDimPDs:
  345|    488|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  346|    488|  return scalar::utf32_to_utf16::convert<endianness::BIG>(buf, len,
  347|    488|                                                          utf16_output);
  348|    488|}
_ZNK7simdutf8fallback14implementation36convert_utf32_to_utf16le_with_errorsEPKDimPDs:
  351|    748|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  352|    748|  return scalar::utf32_to_utf16::convert_with_errors<endianness::LITTLE>(
  353|    748|      buf, len, utf16_output);
  354|    748|}
_ZNK7simdutf8fallback14implementation36convert_utf32_to_utf16be_with_errorsEPKDimPDs:
  357|    670|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  358|    670|  return scalar::utf32_to_utf16::convert_with_errors<endianness::BIG>(
  359|    670|      buf, len, utf16_output);
  360|    670|}
_ZNK7simdutf8fallback14implementation30convert_valid_utf32_to_utf16leEPKDimPDs:
  363|    320|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  364|    320|  return scalar::utf32_to_utf16::convert_valid<endianness::LITTLE>(
  365|    320|      buf, len, utf16_output);
  366|    320|}
_ZNK7simdutf8fallback14implementation30convert_valid_utf32_to_utf16beEPKDimPDs:
  369|    132|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  370|    132|  return scalar::utf32_to_utf16::convert_valid<endianness::BIG>(buf, len,
  371|    132|                                                                utf16_output);
  372|    132|}
_ZNK7simdutf8fallback14implementation24convert_utf16le_to_utf32EPKDsmPDi:
  375|    302|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  376|    302|  return scalar::utf16_to_utf32::convert<endianness::LITTLE>(buf, len,
  377|    302|                                                             utf32_output);
  378|    302|}
_ZNK7simdutf8fallback14implementation24convert_utf16be_to_utf32EPKDsmPDi:
  381|    278|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  382|    278|  return scalar::utf16_to_utf32::convert<endianness::BIG>(buf, len,
  383|    278|                                                          utf32_output);
  384|    278|}
_ZNK7simdutf8fallback14implementation36convert_utf16le_to_utf32_with_errorsEPKDsmPDi:
  387|    442|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  388|    442|  return scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
  389|    442|      buf, len, utf32_output);
  390|    442|}
_ZNK7simdutf8fallback14implementation36convert_utf16be_to_utf32_with_errorsEPKDsmPDi:
  393|    360|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  394|    360|  return scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
  395|    360|      buf, len, utf32_output);
  396|    360|}
_ZNK7simdutf8fallback14implementation30convert_valid_utf16le_to_utf32EPKDsmPDi:
  399|    128|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  400|    128|  return scalar::utf16_to_utf32::convert_valid<endianness::LITTLE>(
  401|    128|      buf, len, utf32_output);
  402|    128|}
_ZNK7simdutf8fallback14implementation30convert_valid_utf16be_to_utf32EPKDsmPDi:
  405|    180|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  406|    180|  return scalar::utf16_to_utf32::convert_valid<endianness::BIG>(buf, len,
  407|    180|                                                                utf32_output);
  408|    180|}
_ZNK7simdutf8fallback14implementation13count_utf16leEPKDsm:
  419|  1.43k|    const char16_t *input, size_t length) const noexcept {
  420|  1.43k|  return scalar::utf16::count_code_points<endianness::LITTLE>(input, length);
  421|  1.43k|}
_ZNK7simdutf8fallback14implementation13count_utf16beEPKDsm:
  424|  1.32k|    const char16_t *input, size_t length) const noexcept {
  425|  1.32k|  return scalar::utf16::count_code_points<endianness::BIG>(input, length);
  426|  1.32k|}
_ZNK7simdutf8fallback14implementation10count_utf8EPKcm:
  431|  4.20k|implementation::count_utf8(const char *input, size_t length) const noexcept {
  432|  4.20k|  return scalar::utf8::count_code_points(input, length);
  433|  4.20k|}
_ZNK7simdutf8fallback14implementation23latin1_length_from_utf8EPKcm:
  443|    720|    const char *buf, size_t len) const noexcept {
  444|    720|  return scalar::utf8::count_code_points(buf, len);
  445|    720|}
_ZNK7simdutf8fallback14implementation23utf8_length_from_latin1EPKcm:
  450|    280|    const char *input, size_t length) const noexcept {
  451|    280|  return scalar::latin1_to_utf8::utf8_length_from_latin1(input, length);
  452|    280|}
_ZNK7simdutf8fallback14implementation24utf8_length_from_utf16leEPKDsm:
  457|    793|    const char16_t *input, size_t length) const noexcept {
  458|    793|  return scalar::utf16::utf8_length_from_utf16<endianness::LITTLE>(input,
  459|    793|                                                                   length);
  460|    793|}
_ZNK7simdutf8fallback14implementation24utf8_length_from_utf16beEPKDsm:
  463|    723|    const char16_t *input, size_t length) const noexcept {
  464|    723|  return scalar::utf16::utf8_length_from_utf16<endianness::BIG>(input, length);
  465|    723|}
_ZNK7simdutf8fallback14implementation25utf32_length_from_utf16leEPKDsm:
  470|    442|    const char16_t *input, size_t length) const noexcept {
  471|    442|  return scalar::utf16::utf32_length_from_utf16<endianness::LITTLE>(input,
  472|    442|                                                                    length);
  473|    442|}
_ZNK7simdutf8fallback14implementation25utf32_length_from_utf16beEPKDsm:
  476|    426|    const char16_t *input, size_t length) const noexcept {
  477|    426|  return scalar::utf16::utf32_length_from_utf16<endianness::BIG>(input, length);
  478|    426|}
_ZNK7simdutf8fallback14implementation22utf16_length_from_utf8EPKcm:
  483|  2.28k|    const char *input, size_t length) const noexcept {
  484|  2.28k|  return scalar::utf8::utf16_length_from_utf8(input, length);
  485|  2.28k|}
_ZNK7simdutf8fallback14implementation22utf8_length_from_utf32EPKDim:
  518|    913|    const char32_t *input, size_t length) const noexcept {
  519|    913|  return scalar::utf32::utf8_length_from_utf32(input, length);
  520|    913|}
_ZNK7simdutf8fallback14implementation23utf16_length_from_utf32EPKDim:
  525|  1.43k|    const char32_t *input, size_t length) const noexcept {
  526|  1.43k|  return scalar::utf32::utf16_length_from_utf32(input, length);
  527|  1.43k|}
_ZNK7simdutf8fallback14implementation22utf32_length_from_utf8EPKcm:
  532|  1.20k|    const char *input, size_t length) const noexcept {
  533|  1.20k|  return scalar::utf8::count_code_points(input, length);
  534|  1.20k|}

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

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1617count_code_pointsILNS_10endiannessE0EEEmPKDsm:
    8|  1.87k|                                               size_t size) {
    9|  1.87k|  size_t pos = 0;
   10|  1.87k|  size_t count = 0;
   11|   963k|  for (; pos < size / 32 * 32; pos += 32) {
  ------------------
  |  Branch (11:10): [True: 961k, False: 1.87k]
  ------------------
   12|   961k|    simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
   13|       |    if constexpr (!match_system(big_endian)) {
   14|       |      input.swap_bytes();
   15|       |    }
   16|   961k|    uint64_t not_pair = input.not_in_range(0xDC00, 0xDFFF);
   17|   961k|    count += count_ones(not_pair) / 2;
   18|   961k|  }
   19|  1.87k|  return count +
   20|  1.87k|         scalar::utf16::count_code_points<big_endian>(in + pos, size - pos);
   21|  1.87k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1617count_code_pointsILNS_10endiannessE1EEEmPKDsm:
    8|  1.74k|                                               size_t size) {
    9|  1.74k|  size_t pos = 0;
   10|  1.74k|  size_t count = 0;
   11|  1.13M|  for (; pos < size / 32 * 32; pos += 32) {
  ------------------
  |  Branch (11:10): [True: 1.13M, False: 1.74k]
  ------------------
   12|  1.13M|    simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
   13|  1.13M|    if constexpr (!match_system(big_endian)) {
   14|  1.13M|      input.swap_bytes();
   15|  1.13M|    }
   16|  1.13M|    uint64_t not_pair = input.not_in_range(0xDC00, 0xDFFF);
   17|  1.13M|    count += count_ones(not_pair) / 2;
   18|  1.13M|  }
   19|  1.74k|  return count +
   20|  1.74k|         scalar::utf16::count_code_points<big_endian>(in + pos, size - pos);
   21|  1.74k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1623utf32_length_from_utf16ILNS_10endiannessE0EEEmPKDsm:
   51|    442|                                                     size_t size) {
   52|    442|  return count_code_points<big_endian>(in, size);
   53|    442|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1623utf32_length_from_utf16ILNS_10endiannessE1EEEmPKDsm:
   51|    426|                                                     size_t size) {
   52|    426|  return count_code_points<big_endian>(in, size);
   53|    426|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1617count_code_pointsILNS_10endiannessE0EEEmPKDsm:
    8|  1.87k|                                               size_t size) {
    9|  1.87k|  size_t pos = 0;
   10|  1.87k|  size_t count = 0;
   11|   963k|  for (; pos < size / 32 * 32; pos += 32) {
  ------------------
  |  Branch (11:10): [True: 961k, False: 1.87k]
  ------------------
   12|   961k|    simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
   13|       |    if constexpr (!match_system(big_endian)) {
   14|       |      input.swap_bytes();
   15|       |    }
   16|   961k|    uint64_t not_pair = input.not_in_range(0xDC00, 0xDFFF);
   17|   961k|    count += count_ones(not_pair) / 2;
   18|   961k|  }
   19|  1.87k|  return count +
   20|  1.87k|         scalar::utf16::count_code_points<big_endian>(in + pos, size - pos);
   21|  1.87k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1617count_code_pointsILNS_10endiannessE1EEEmPKDsm:
    8|  1.74k|                                               size_t size) {
    9|  1.74k|  size_t pos = 0;
   10|  1.74k|  size_t count = 0;
   11|  1.13M|  for (; pos < size / 32 * 32; pos += 32) {
  ------------------
  |  Branch (11:10): [True: 1.13M, False: 1.74k]
  ------------------
   12|  1.13M|    simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
   13|  1.13M|    if constexpr (!match_system(big_endian)) {
   14|  1.13M|      input.swap_bytes();
   15|  1.13M|    }
   16|  1.13M|    uint64_t not_pair = input.not_in_range(0xDC00, 0xDFFF);
   17|  1.13M|    count += count_ones(not_pair) / 2;
   18|  1.13M|  }
   19|  1.74k|  return count +
   20|  1.74k|         scalar::utf16::count_code_points<big_endian>(in + pos, size - pos);
   21|  1.74k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1623utf32_length_from_utf16ILNS_10endiannessE0EEEmPKDsm:
   51|    442|                                                     size_t size) {
   52|    442|  return count_code_points<big_endian>(in, size);
   53|    442|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1623utf32_length_from_utf16ILNS_10endiannessE1EEEmPKDsm:
   51|    426|                                                     size_t size) {
   52|    426|  return count_code_points<big_endian>(in, size);
   53|    426|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1631utf8_length_from_utf16_bytemaskILNS_10endiannessE0EEEmPKDsm:
   10|    793|                                                             size_t size) {
   11|    793|  size_t pos = 0;
   12|       |
   13|    793|  using vector_u16 = simd16<uint16_t>;
   14|    793|  constexpr size_t N = vector_u16::ELEMENTS;
   15|       |
   16|    793|  const auto one = vector_u16::splat(1);
   17|       |
   18|    793|  auto v_count = vector_u16::zero();
   19|       |
   20|       |  // each char16 yields at least one byte
   21|    793|  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|    793|  constexpr size_t max_iterations = 65535 / 2;
   26|    793|  size_t iteration = max_iterations;
   27|       |
   28|  1.08M|  for (; pos < size / N * N; pos += N) {
  ------------------
  |  Branch (28:10): [True: 1.08M, False: 793]
  ------------------
   29|  1.08M|    auto input = vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos));
   30|       |    if constexpr (!match_system(big_endian)) {
   31|       |      input = input.swap_bytes();
   32|       |    }
   33|       |    // 0xd800 .. 0xdbff - low surrogate
   34|       |    // 0xdc00 .. 0xdfff - high surrogate
   35|  1.08M|    const auto is_surrogate = ((input & uint16_t(0xf800)) == uint16_t(0xd800));
   36|       |
   37|       |    // c0 - chars that yield 2- or 3-byte UTF-8 codes
   38|  1.08M|    const auto c0 = min(input & uint16_t(0xff80), one);
   39|       |
   40|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
   41|  1.08M|    const auto c1 = min(input & uint16_t(0xf800), one);
   42|       |
   43|       |    /*
   44|       |        Explanation how the counting works.
   45|       |
   46|       |        In the case of a non-surrogate character we count:
   47|       |        * always 1 -- see how `count` is initialized above;
   48|       |        * c0 = 1 if the current char yields 2 or 3 bytes;
   49|       |        * c1 = 1 if the current char yields 3 bytes.
   50|       |
   51|       |        Thus, we always have correct count for the current char:
   52|       |        from 1, 2 or 3 bytes.
   53|       |
   54|       |        A trickier part is how we count surrogate pairs. Whether
   55|       |        we encounter a surrogate (low or high), we count it as
   56|       |        3 chars and then minus 1 (`is_surrogate` is -1 or 0).
   57|       |        Each surrogate char yields 2. A surrogate pair, that
   58|       |        is a low surrogate followed by a high one, yields
   59|       |        the expected 4 bytes.
   60|       |
   61|       |        It also correctly handles cases when low surrogate is
   62|       |        processed by the this loop, but high surrogate is counted
   63|       |        by the scalar procedure. The scalar procedure uses exactly
   64|       |        the described approach, thanks to that for valid UTF-16
   65|       |        strings it always count correctly.
   66|       |    */
   67|  1.08M|    v_count += c0;
   68|  1.08M|    v_count += c1;
   69|  1.08M|    v_count += vector_u16(is_surrogate);
   70|       |
   71|  1.08M|    iteration -= 1;
   72|  1.08M|    if (iteration == 0) {
  ------------------
  |  Branch (72:9): [True: 18, False: 1.08M]
  ------------------
   73|     18|      count += v_count.sum();
   74|     18|      v_count = vector_u16::zero();
   75|     18|      iteration = max_iterations;
   76|     18|    }
   77|  1.08M|  }
   78|       |
   79|    793|  if (iteration > 0) {
  ------------------
  |  Branch (79:7): [True: 793, False: 0]
  ------------------
   80|    793|    count += v_count.sum();
   81|    793|  }
   82|       |
   83|    793|  return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos,
   84|    793|                                                                   size - pos);
   85|    793|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1631utf8_length_from_utf16_bytemaskILNS_10endiannessE1EEEmPKDsm:
   10|    723|                                                             size_t size) {
   11|    723|  size_t pos = 0;
   12|       |
   13|    723|  using vector_u16 = simd16<uint16_t>;
   14|    723|  constexpr size_t N = vector_u16::ELEMENTS;
   15|       |
   16|    723|  const auto one = vector_u16::splat(1);
   17|       |
   18|    723|  auto v_count = vector_u16::zero();
   19|       |
   20|       |  // each char16 yields at least one byte
   21|    723|  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|    723|  constexpr size_t max_iterations = 65535 / 2;
   26|    723|  size_t iteration = max_iterations;
   27|       |
   28|   950k|  for (; pos < size / N * N; pos += N) {
  ------------------
  |  Branch (28:10): [True: 949k, False: 723]
  ------------------
   29|   949k|    auto input = vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos));
   30|   949k|    if constexpr (!match_system(big_endian)) {
   31|   949k|      input = input.swap_bytes();
   32|   949k|    }
   33|       |    // 0xd800 .. 0xdbff - low surrogate
   34|       |    // 0xdc00 .. 0xdfff - high surrogate
   35|   949k|    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|   949k|    const auto c0 = min(input & uint16_t(0xff80), one);
   39|       |
   40|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
   41|   949k|    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|   949k|    v_count += c0;
   68|   949k|    v_count += c1;
   69|   949k|    v_count += vector_u16(is_surrogate);
   70|       |
   71|   949k|    iteration -= 1;
   72|   949k|    if (iteration == 0) {
  ------------------
  |  Branch (72:9): [True: 17, False: 949k]
  ------------------
   73|     17|      count += v_count.sum();
   74|     17|      v_count = vector_u16::zero();
   75|     17|      iteration = max_iterations;
   76|     17|    }
   77|   949k|  }
   78|       |
   79|    723|  if (iteration > 0) {
  ------------------
  |  Branch (79:7): [True: 723, False: 0]
  ------------------
   80|    723|    count += v_count.sum();
   81|    723|  }
   82|       |
   83|    723|  return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos,
   84|    723|                                                                   size - pos);
   85|    723|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1631utf8_length_from_utf16_bytemaskILNS_10endiannessE0EEEmPKDsm:
   10|    793|                                                             size_t size) {
   11|    793|  size_t pos = 0;
   12|       |
   13|    793|  using vector_u16 = simd16<uint16_t>;
   14|    793|  constexpr size_t N = vector_u16::ELEMENTS;
   15|       |
   16|    793|  const auto one = vector_u16::splat(1);
   17|       |
   18|    793|  auto v_count = vector_u16::zero();
   19|       |
   20|       |  // each char16 yields at least one byte
   21|    793|  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|    793|  constexpr size_t max_iterations = 65535 / 2;
   26|    793|  size_t iteration = max_iterations;
   27|       |
   28|  2.16M|  for (; pos < size / N * N; pos += N) {
  ------------------
  |  Branch (28:10): [True: 2.16M, False: 793]
  ------------------
   29|  2.16M|    auto input = vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos));
   30|       |    if constexpr (!match_system(big_endian)) {
   31|       |      input = input.swap_bytes();
   32|       |    }
   33|       |    // 0xd800 .. 0xdbff - low surrogate
   34|       |    // 0xdc00 .. 0xdfff - high surrogate
   35|  2.16M|    const auto is_surrogate = ((input & uint16_t(0xf800)) == uint16_t(0xd800));
   36|       |
   37|       |    // c0 - chars that yield 2- or 3-byte UTF-8 codes
   38|  2.16M|    const auto c0 = min(input & uint16_t(0xff80), one);
   39|       |
   40|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
   41|  2.16M|    const auto c1 = min(input & uint16_t(0xf800), one);
   42|       |
   43|       |    /*
   44|       |        Explanation how the counting works.
   45|       |
   46|       |        In the case of a non-surrogate character we count:
   47|       |        * always 1 -- see how `count` is initialized above;
   48|       |        * c0 = 1 if the current char yields 2 or 3 bytes;
   49|       |        * c1 = 1 if the current char yields 3 bytes.
   50|       |
   51|       |        Thus, we always have correct count for the current char:
   52|       |        from 1, 2 or 3 bytes.
   53|       |
   54|       |        A trickier part is how we count surrogate pairs. Whether
   55|       |        we encounter a surrogate (low or high), we count it as
   56|       |        3 chars and then minus 1 (`is_surrogate` is -1 or 0).
   57|       |        Each surrogate char yields 2. A surrogate pair, that
   58|       |        is a low surrogate followed by a high one, yields
   59|       |        the expected 4 bytes.
   60|       |
   61|       |        It also correctly handles cases when low surrogate is
   62|       |        processed by the this loop, but high surrogate is counted
   63|       |        by the scalar procedure. The scalar procedure uses exactly
   64|       |        the described approach, thanks to that for valid UTF-16
   65|       |        strings it always count correctly.
   66|       |    */
   67|  2.16M|    v_count += c0;
   68|  2.16M|    v_count += c1;
   69|  2.16M|    v_count += vector_u16(is_surrogate);
   70|       |
   71|  2.16M|    iteration -= 1;
   72|  2.16M|    if (iteration == 0) {
  ------------------
  |  Branch (72:9): [True: 49, False: 2.16M]
  ------------------
   73|     49|      count += v_count.sum();
   74|     49|      v_count = vector_u16::zero();
   75|     49|      iteration = max_iterations;
   76|     49|    }
   77|  2.16M|  }
   78|       |
   79|    793|  if (iteration > 0) {
  ------------------
  |  Branch (79:7): [True: 793, False: 0]
  ------------------
   80|    793|    count += v_count.sum();
   81|    793|  }
   82|       |
   83|    793|  return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos,
   84|    793|                                                                   size - pos);
   85|    793|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1631utf8_length_from_utf16_bytemaskILNS_10endiannessE1EEEmPKDsm:
   10|    723|                                                             size_t size) {
   11|    723|  size_t pos = 0;
   12|       |
   13|    723|  using vector_u16 = simd16<uint16_t>;
   14|    723|  constexpr size_t N = vector_u16::ELEMENTS;
   15|       |
   16|    723|  const auto one = vector_u16::splat(1);
   17|       |
   18|    723|  auto v_count = vector_u16::zero();
   19|       |
   20|       |  // each char16 yields at least one byte
   21|    723|  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|    723|  constexpr size_t max_iterations = 65535 / 2;
   26|    723|  size_t iteration = max_iterations;
   27|       |
   28|  1.89M|  for (; pos < size / N * N; pos += N) {
  ------------------
  |  Branch (28:10): [True: 1.89M, False: 723]
  ------------------
   29|  1.89M|    auto input = vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos));
   30|  1.89M|    if constexpr (!match_system(big_endian)) {
   31|  1.89M|      input = input.swap_bytes();
   32|  1.89M|    }
   33|       |    // 0xd800 .. 0xdbff - low surrogate
   34|       |    // 0xdc00 .. 0xdfff - high surrogate
   35|  1.89M|    const auto is_surrogate = ((input & uint16_t(0xf800)) == uint16_t(0xd800));
   36|       |
   37|       |    // c0 - chars that yield 2- or 3-byte UTF-8 codes
   38|  1.89M|    const auto c0 = min(input & uint16_t(0xff80), one);
   39|       |
   40|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
   41|  1.89M|    const auto c1 = min(input & uint16_t(0xf800), one);
   42|       |
   43|       |    /*
   44|       |        Explanation how the counting works.
   45|       |
   46|       |        In the case of a non-surrogate character we count:
   47|       |        * always 1 -- see how `count` is initialized above;
   48|       |        * c0 = 1 if the current char yields 2 or 3 bytes;
   49|       |        * c1 = 1 if the current char yields 3 bytes.
   50|       |
   51|       |        Thus, we always have correct count for the current char:
   52|       |        from 1, 2 or 3 bytes.
   53|       |
   54|       |        A trickier part is how we count surrogate pairs. Whether
   55|       |        we encounter a surrogate (low or high), we count it as
   56|       |        3 chars and then minus 1 (`is_surrogate` is -1 or 0).
   57|       |        Each surrogate char yields 2. A surrogate pair, that
   58|       |        is a low surrogate followed by a high one, yields
   59|       |        the expected 4 bytes.
   60|       |
   61|       |        It also correctly handles cases when low surrogate is
   62|       |        processed by the this loop, but high surrogate is counted
   63|       |        by the scalar procedure. The scalar procedure uses exactly
   64|       |        the described approach, thanks to that for valid UTF-16
   65|       |        strings it always count correctly.
   66|       |    */
   67|  1.89M|    v_count += c0;
   68|  1.89M|    v_count += c1;
   69|  1.89M|    v_count += vector_u16(is_surrogate);
   70|       |
   71|  1.89M|    iteration -= 1;
   72|  1.89M|    if (iteration == 0) {
  ------------------
  |  Branch (72:9): [True: 45, False: 1.89M]
  ------------------
   73|     45|      count += v_count.sum();
   74|     45|      v_count = vector_u16::zero();
   75|     45|      iteration = max_iterations;
   76|     45|    }
   77|  1.89M|  }
   78|       |
   79|    723|  if (iteration > 0) {
  ------------------
  |  Branch (79:7): [True: 723, False: 0]
  ------------------
   80|    723|    count += v_count.sum();
   81|    723|  }
   82|       |
   83|    723|  return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos,
   84|    723|                                                                   size - pos);
   85|    723|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf3222utf8_length_from_utf32EPKDim:
   11|    913|                                                    size_t length) {
   12|    913|  using vector_u32 = simd32<uint32_t>;
   13|       |
   14|    913|  const char32_t *start = input;
   15|       |
   16|       |  // we add up to three ones in a single iteration (see the vectorized loop in
   17|       |  // section #2 below)
   18|    913|  const size_t max_increment = 3;
   19|       |
   20|    913|  const size_t N = vector_u32::ELEMENTS;
   21|       |
   22|       |#if SIMDUTF_SIMD_HAS_UNSIGNED_CMP
   23|       |  const auto v_0000007f = vector_u32::splat(0x0000007f);
   24|       |  const auto v_000007ff = vector_u32::splat(0x000007ff);
   25|       |  const auto v_0000ffff = vector_u32::splat(0x0000ffff);
   26|       |#else
   27|    913|  const auto v_ffffff80 = vector_u32::splat(0xffffff80);
   28|    913|  const auto v_fffff800 = vector_u32::splat(0xfffff800);
   29|    913|  const auto v_ffff0000 = vector_u32::splat(0xffff0000);
   30|    913|  const auto one = vector_u32::splat(1);
   31|    913|#endif // SIMDUTF_SIMD_HAS_UNSIGNED_CMP
   32|       |
   33|    913|  size_t counter = 0;
   34|       |
   35|       |  // 1. vectorized loop unrolled 4 times
   36|    913|  {
   37|       |    // we use vector of uint32 counters, this is why this limit is used
   38|    913|    const size_t max_iterations =
   39|    913|        std::numeric_limits<uint32_t>::max() / (max_increment * 4);
   40|    913|    size_t blocks = length / (N * 4);
   41|    913|    length -= blocks * (N * 4);
   42|  1.13k|    while (blocks != 0) {
  ------------------
  |  Branch (42:12): [True: 219, False: 913]
  ------------------
   43|    219|      const size_t iterations = min(blocks, max_iterations);
   44|    219|      blocks -= iterations;
   45|       |
   46|    219|      simd32<uint32_t> acc = vector_u32::zero();
   47|  91.7k|      for (size_t i = 0; i < iterations; i++) {
  ------------------
  |  Branch (47:26): [True: 91.5k, False: 219]
  ------------------
   48|  91.5k|        const auto in0 = vector_u32(input + 0 * N);
   49|  91.5k|        const auto in1 = vector_u32(input + 1 * N);
   50|  91.5k|        const auto in2 = vector_u32(input + 2 * N);
   51|  91.5k|        const auto in3 = vector_u32(input + 3 * N);
   52|       |
   53|       |#if SIMDUTF_SIMD_HAS_UNSIGNED_CMP
   54|       |        acc -= as_vector_u32(in0 > v_0000007f);
   55|       |        acc -= as_vector_u32(in1 > v_0000007f);
   56|       |        acc -= as_vector_u32(in2 > v_0000007f);
   57|       |        acc -= as_vector_u32(in3 > v_0000007f);
   58|       |
   59|       |        acc -= as_vector_u32(in0 > v_000007ff);
   60|       |        acc -= as_vector_u32(in1 > v_000007ff);
   61|       |        acc -= as_vector_u32(in2 > v_000007ff);
   62|       |        acc -= as_vector_u32(in3 > v_000007ff);
   63|       |
   64|       |        acc -= as_vector_u32(in0 > v_0000ffff);
   65|       |        acc -= as_vector_u32(in1 > v_0000ffff);
   66|       |        acc -= as_vector_u32(in2 > v_0000ffff);
   67|       |        acc -= as_vector_u32(in3 > v_0000ffff);
   68|       |#else
   69|  91.5k|        acc += min(one, in0 & v_ffffff80);
   70|  91.5k|        acc += min(one, in1 & v_ffffff80);
   71|  91.5k|        acc += min(one, in2 & v_ffffff80);
   72|  91.5k|        acc += min(one, in3 & v_ffffff80);
   73|       |
   74|  91.5k|        acc += min(one, in0 & v_fffff800);
   75|  91.5k|        acc += min(one, in1 & v_fffff800);
   76|  91.5k|        acc += min(one, in2 & v_fffff800);
   77|  91.5k|        acc += min(one, in3 & v_fffff800);
   78|       |
   79|  91.5k|        acc += min(one, in0 & v_ffff0000);
   80|  91.5k|        acc += min(one, in1 & v_ffff0000);
   81|  91.5k|        acc += min(one, in2 & v_ffff0000);
   82|  91.5k|        acc += min(one, in3 & v_ffff0000);
   83|  91.5k|#endif // SIMDUTF_SIMD_HAS_UNSIGNED_CMP
   84|       |
   85|  91.5k|        input += 4 * N;
   86|  91.5k|      }
   87|       |
   88|    219|      counter += acc.sum();
   89|    219|    }
   90|    913|  }
   91|       |
   92|       |  // 2. vectorized loop for tail
   93|    913|  {
   94|    913|    const size_t max_iterations =
   95|    913|        std::numeric_limits<uint32_t>::max() / max_increment;
   96|    913|    size_t blocks = length / N;
   97|    913|    length -= blocks * N;
   98|  1.35k|    while (blocks != 0) {
  ------------------
  |  Branch (98:12): [True: 442, False: 913]
  ------------------
   99|    442|      const size_t iterations = min(blocks, max_iterations);
  100|    442|      blocks -= iterations;
  101|       |
  102|    442|      auto acc = vector_u32::zero();
  103|  1.54k|      for (size_t i = 0; i < iterations; i++) {
  ------------------
  |  Branch (103:26): [True: 1.10k, False: 442]
  ------------------
  104|  1.10k|        const auto in = vector_u32(input);
  105|       |
  106|       |#if SIMDUTF_SIMD_HAS_UNSIGNED_CMP
  107|       |        acc -= as_vector_u32(in > v_0000007f);
  108|       |        acc -= as_vector_u32(in > v_000007ff);
  109|       |        acc -= as_vector_u32(in > v_0000ffff);
  110|       |#else
  111|  1.10k|        acc += min(one, in & v_ffffff80);
  112|  1.10k|        acc += min(one, in & v_fffff800);
  113|  1.10k|        acc += min(one, in & v_ffff0000);
  114|  1.10k|#endif // SIMDUTF_SIMD_HAS_UNSIGNED_CMP
  115|       |
  116|  1.10k|        input += N;
  117|  1.10k|      }
  118|       |
  119|    442|      counter += acc.sum();
  120|    442|    }
  121|    913|  }
  122|       |
  123|    913|  const size_t consumed = input - start;
  124|    913|  if (consumed != 0) {
  ------------------
  |  Branch (124:7): [True: 515, False: 398]
  ------------------
  125|       |    // We don't count 0th bytes in the vectorized loops above, this
  126|       |    // is why we need to count them in the end.
  127|    515|    counter += consumed;
  128|    515|  }
  129|       |
  130|    913|  return counter + scalar::utf32::utf8_length_from_utf32(input, length);
  131|    913|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf323minImEET_S4_S4_:
    8|    661|template <typename T> T min(T a, T b) { return a <= b ? a : b; }
  ------------------
  |  Branch (8:48): [True: 661, False: 0]
  ------------------
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf3222utf8_length_from_utf32EPKDim:
   11|    913|                                                    size_t length) {
   12|    913|  using vector_u32 = simd32<uint32_t>;
   13|       |
   14|    913|  const char32_t *start = input;
   15|       |
   16|       |  // we add up to three ones in a single iteration (see the vectorized loop in
   17|       |  // section #2 below)
   18|    913|  const size_t max_increment = 3;
   19|       |
   20|    913|  const size_t N = vector_u32::ELEMENTS;
   21|       |
   22|       |#if SIMDUTF_SIMD_HAS_UNSIGNED_CMP
   23|       |  const auto v_0000007f = vector_u32::splat(0x0000007f);
   24|       |  const auto v_000007ff = vector_u32::splat(0x000007ff);
   25|       |  const auto v_0000ffff = vector_u32::splat(0x0000ffff);
   26|       |#else
   27|    913|  const auto v_ffffff80 = vector_u32::splat(0xffffff80);
   28|    913|  const auto v_fffff800 = vector_u32::splat(0xfffff800);
   29|    913|  const auto v_ffff0000 = vector_u32::splat(0xffff0000);
   30|    913|  const auto one = vector_u32::splat(1);
   31|    913|#endif // SIMDUTF_SIMD_HAS_UNSIGNED_CMP
   32|       |
   33|    913|  size_t counter = 0;
   34|       |
   35|       |  // 1. vectorized loop unrolled 4 times
   36|    913|  {
   37|       |    // we use vector of uint32 counters, this is why this limit is used
   38|    913|    const size_t max_iterations =
   39|    913|        std::numeric_limits<uint32_t>::max() / (max_increment * 4);
   40|    913|    size_t blocks = length / (N * 4);
   41|    913|    length -= blocks * (N * 4);
   42|  1.40k|    while (blocks != 0) {
  ------------------
  |  Branch (42:12): [True: 491, False: 913]
  ------------------
   43|    491|      const size_t iterations = min(blocks, max_iterations);
   44|    491|      blocks -= iterations;
   45|       |
   46|    491|      simd32<uint32_t> acc = vector_u32::zero();
   47|   183k|      for (size_t i = 0; i < iterations; i++) {
  ------------------
  |  Branch (47:26): [True: 183k, False: 491]
  ------------------
   48|   183k|        const auto in0 = vector_u32(input + 0 * N);
   49|   183k|        const auto in1 = vector_u32(input + 1 * N);
   50|   183k|        const auto in2 = vector_u32(input + 2 * N);
   51|   183k|        const auto in3 = vector_u32(input + 3 * N);
   52|       |
   53|       |#if SIMDUTF_SIMD_HAS_UNSIGNED_CMP
   54|       |        acc -= as_vector_u32(in0 > v_0000007f);
   55|       |        acc -= as_vector_u32(in1 > v_0000007f);
   56|       |        acc -= as_vector_u32(in2 > v_0000007f);
   57|       |        acc -= as_vector_u32(in3 > v_0000007f);
   58|       |
   59|       |        acc -= as_vector_u32(in0 > v_000007ff);
   60|       |        acc -= as_vector_u32(in1 > v_000007ff);
   61|       |        acc -= as_vector_u32(in2 > v_000007ff);
   62|       |        acc -= as_vector_u32(in3 > v_000007ff);
   63|       |
   64|       |        acc -= as_vector_u32(in0 > v_0000ffff);
   65|       |        acc -= as_vector_u32(in1 > v_0000ffff);
   66|       |        acc -= as_vector_u32(in2 > v_0000ffff);
   67|       |        acc -= as_vector_u32(in3 > v_0000ffff);
   68|       |#else
   69|   183k|        acc += min(one, in0 & v_ffffff80);
   70|   183k|        acc += min(one, in1 & v_ffffff80);
   71|   183k|        acc += min(one, in2 & v_ffffff80);
   72|   183k|        acc += min(one, in3 & v_ffffff80);
   73|       |
   74|   183k|        acc += min(one, in0 & v_fffff800);
   75|   183k|        acc += min(one, in1 & v_fffff800);
   76|   183k|        acc += min(one, in2 & v_fffff800);
   77|   183k|        acc += min(one, in3 & v_fffff800);
   78|       |
   79|   183k|        acc += min(one, in0 & v_ffff0000);
   80|   183k|        acc += min(one, in1 & v_ffff0000);
   81|   183k|        acc += min(one, in2 & v_ffff0000);
   82|   183k|        acc += min(one, in3 & v_ffff0000);
   83|   183k|#endif // SIMDUTF_SIMD_HAS_UNSIGNED_CMP
   84|       |
   85|   183k|        input += 4 * N;
   86|   183k|      }
   87|       |
   88|    491|      counter += acc.sum();
   89|    491|    }
   90|    913|  }
   91|       |
   92|       |  // 2. vectorized loop for tail
   93|    913|  {
   94|    913|    const size_t max_iterations =
   95|    913|        std::numeric_limits<uint32_t>::max() / max_increment;
   96|    913|    size_t blocks = length / N;
   97|    913|    length -= blocks * N;
   98|  1.42k|    while (blocks != 0) {
  ------------------
  |  Branch (98:12): [True: 510, False: 913]
  ------------------
   99|    510|      const size_t iterations = min(blocks, max_iterations);
  100|    510|      blocks -= iterations;
  101|       |
  102|    510|      auto acc = vector_u32::zero();
  103|  1.74k|      for (size_t i = 0; i < iterations; i++) {
  ------------------
  |  Branch (103:26): [True: 1.23k, False: 510]
  ------------------
  104|  1.23k|        const auto in = vector_u32(input);
  105|       |
  106|       |#if SIMDUTF_SIMD_HAS_UNSIGNED_CMP
  107|       |        acc -= as_vector_u32(in > v_0000007f);
  108|       |        acc -= as_vector_u32(in > v_000007ff);
  109|       |        acc -= as_vector_u32(in > v_0000ffff);
  110|       |#else
  111|  1.23k|        acc += min(one, in & v_ffffff80);
  112|  1.23k|        acc += min(one, in & v_fffff800);
  113|  1.23k|        acc += min(one, in & v_ffff0000);
  114|  1.23k|#endif // SIMDUTF_SIMD_HAS_UNSIGNED_CMP
  115|       |
  116|  1.23k|        input += N;
  117|  1.23k|      }
  118|       |
  119|    510|      counter += acc.sum();
  120|    510|    }
  121|    913|  }
  122|       |
  123|    913|  const size_t consumed = input - start;
  124|    913|  if (consumed != 0) {
  ------------------
  |  Branch (124:7): [True: 558, False: 355]
  ------------------
  125|       |    // We don't count 0th bytes in the vectorized loops above, this
  126|       |    // is why we need to count them in the end.
  127|    558|    counter += consumed;
  128|    558|  }
  129|       |
  130|    913|  return counter + scalar::utf32::utf8_length_from_utf32(input, length);
  131|    913|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf323minImEET_S4_S4_:
    8|  1.00k|template <typename T> T min(T a, T b) { return a <= b ? a : b; }
  ------------------
  |  Branch (8:48): [True: 1.00k, False: 0]
  ------------------

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14utf826count_code_points_bytemaskEPKcm:
   21|  4.92k|count_code_points_bytemask(const char *in, size_t size) {
   22|  4.92k|  using vector_i8 = simd8<int8_t>;
   23|  4.92k|  using vector_u8 = simd8<uint8_t>;
   24|  4.92k|  using vector_u64 = simd64<uint64_t>;
   25|       |
   26|  4.92k|  constexpr size_t N = vector_i8::SIZE;
   27|  4.92k|  constexpr size_t max_iterations = 255 / 4;
   28|       |
   29|  4.92k|  size_t pos = 0;
   30|  4.92k|  size_t count = 0;
   31|       |
   32|  4.92k|  auto counters = vector_u64::zero();
   33|  4.92k|  auto local = vector_u8::zero();
   34|  4.92k|  size_t iterations = 0;
   35|  1.92M|  for (; pos + 4 * N <= size; pos += 4 * N) {
  ------------------
  |  Branch (35:10): [True: 1.92M, False: 4.92k]
  ------------------
   36|  1.92M|    const auto input0 =
   37|  1.92M|        simd8<int8_t>::load(reinterpret_cast<const int8_t *>(in + pos + 0 * N));
   38|  1.92M|    const auto input1 =
   39|  1.92M|        simd8<int8_t>::load(reinterpret_cast<const int8_t *>(in + pos + 1 * N));
   40|  1.92M|    const auto input2 =
   41|  1.92M|        simd8<int8_t>::load(reinterpret_cast<const int8_t *>(in + pos + 2 * N));
   42|  1.92M|    const auto input3 =
   43|  1.92M|        simd8<int8_t>::load(reinterpret_cast<const int8_t *>(in + pos + 3 * N));
   44|  1.92M|    const auto mask0 = input0 > int8_t(-65);
   45|  1.92M|    const auto mask1 = input1 > int8_t(-65);
   46|  1.92M|    const auto mask2 = input2 > int8_t(-65);
   47|  1.92M|    const auto mask3 = input3 > int8_t(-65);
   48|       |
   49|  1.92M|    local -= vector_u8(mask0);
   50|  1.92M|    local -= vector_u8(mask1);
   51|  1.92M|    local -= vector_u8(mask2);
   52|  1.92M|    local -= vector_u8(mask3);
   53|       |
   54|  1.92M|    iterations += 1;
   55|  1.92M|    if (iterations == max_iterations) {
  ------------------
  |  Branch (55:9): [True: 30.1k, False: 1.89M]
  ------------------
   56|  30.1k|      counters += sum_8bytes(local);
   57|  30.1k|      local = vector_u8::zero();
   58|  30.1k|      iterations = 0;
   59|  30.1k|    }
   60|  1.92M|  }
   61|       |
   62|  4.92k|  if (iterations > 0) {
  ------------------
  |  Branch (62:7): [True: 1.70k, False: 3.22k]
  ------------------
   63|  1.70k|    count += local.sum_bytes();
   64|  1.70k|  }
   65|       |
   66|  4.92k|  count += counters.sum();
   67|       |
   68|  4.92k|  return count + scalar::utf8::count_code_points(in + pos, size - pos);
   69|  4.92k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14utf817count_code_pointsEPKcm:
    8|  1.20k|simdutf_really_inline size_t count_code_points(const char *in, size_t size) {
    9|  1.20k|  size_t pos = 0;
   10|  1.20k|  size_t count = 0;
   11|   946k|  for (; pos + 64 <= size; pos += 64) {
  ------------------
  |  Branch (11:10): [True: 944k, False: 1.20k]
  ------------------
   12|   944k|    simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
   13|   944k|    uint64_t utf8_continuation_mask = input.gt(-65);
   14|   944k|    count += count_ones(utf8_continuation_mask);
   15|   944k|  }
   16|  1.20k|  return count + scalar::utf8::count_code_points(in + pos, size - pos);
   17|  1.20k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14utf826count_code_points_bytemaskEPKcm:
   21|  4.92k|count_code_points_bytemask(const char *in, size_t size) {
   22|  4.92k|  using vector_i8 = simd8<int8_t>;
   23|  4.92k|  using vector_u8 = simd8<uint8_t>;
   24|  4.92k|  using vector_u64 = simd64<uint64_t>;
   25|       |
   26|  4.92k|  constexpr size_t N = vector_i8::SIZE;
   27|  4.92k|  constexpr size_t max_iterations = 255 / 4;
   28|       |
   29|  4.92k|  size_t pos = 0;
   30|  4.92k|  size_t count = 0;
   31|       |
   32|  4.92k|  auto counters = vector_u64::zero();
   33|  4.92k|  auto local = vector_u8::zero();
   34|  4.92k|  size_t iterations = 0;
   35|  3.84M|  for (; pos + 4 * N <= size; pos += 4 * N) {
  ------------------
  |  Branch (35:10): [True: 3.84M, False: 4.92k]
  ------------------
   36|  3.84M|    const auto input0 =
   37|  3.84M|        simd8<int8_t>::load(reinterpret_cast<const int8_t *>(in + pos + 0 * N));
   38|  3.84M|    const auto input1 =
   39|  3.84M|        simd8<int8_t>::load(reinterpret_cast<const int8_t *>(in + pos + 1 * N));
   40|  3.84M|    const auto input2 =
   41|  3.84M|        simd8<int8_t>::load(reinterpret_cast<const int8_t *>(in + pos + 2 * N));
   42|  3.84M|    const auto input3 =
   43|  3.84M|        simd8<int8_t>::load(reinterpret_cast<const int8_t *>(in + pos + 3 * N));
   44|  3.84M|    const auto mask0 = input0 > int8_t(-65);
   45|  3.84M|    const auto mask1 = input1 > int8_t(-65);
   46|  3.84M|    const auto mask2 = input2 > int8_t(-65);
   47|  3.84M|    const auto mask3 = input3 > int8_t(-65);
   48|       |
   49|  3.84M|    local -= vector_u8(mask0);
   50|  3.84M|    local -= vector_u8(mask1);
   51|  3.84M|    local -= vector_u8(mask2);
   52|  3.84M|    local -= vector_u8(mask3);
   53|       |
   54|  3.84M|    iterations += 1;
   55|  3.84M|    if (iterations == max_iterations) {
  ------------------
  |  Branch (55:9): [True: 60.6k, False: 3.78M]
  ------------------
   56|  60.6k|      counters += sum_8bytes(local);
   57|  60.6k|      local = vector_u8::zero();
   58|  60.6k|      iterations = 0;
   59|  60.6k|    }
   60|  3.84M|  }
   61|       |
   62|  4.92k|  if (iterations > 0) {
  ------------------
  |  Branch (62:7): [True: 3.34k, False: 1.58k]
  ------------------
   63|  3.34k|    count += local.sum_bytes();
   64|  3.34k|  }
   65|       |
   66|  4.92k|  count += counters.sum();
   67|       |
   68|  4.92k|  return count + scalar::utf8::count_code_points(in + pos, size - pos);
   69|  4.92k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14utf817count_code_pointsEPKcm:
    8|  1.20k|simdutf_really_inline size_t count_code_points(const char *in, size_t size) {
    9|  1.20k|  size_t pos = 0;
   10|  1.20k|  size_t count = 0;
   11|   946k|  for (; pos + 64 <= size; pos += 64) {
  ------------------
  |  Branch (11:10): [True: 944k, False: 1.20k]
  ------------------
   12|   944k|    simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
   13|   944k|    uint64_t utf8_continuation_mask = input.gt(-65);
   14|   944k|    count += count_ones(utf8_continuation_mask);
   15|   944k|  }
   16|  1.20k|  return count + scalar::utf8::count_code_points(in + pos, size - pos);
   17|  1.20k|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14utf831utf16_length_from_utf8_bytemaskEPKcm:
    9|  2.28k|                                                             size_t size) {
   10|  2.28k|  using vector_i8 = simd8<int8_t>;
   11|  2.28k|  using vector_u8 = simd8<uint8_t>;
   12|  2.28k|  using vector_u64 = simd64<uint64_t>;
   13|       |
   14|  2.28k|  constexpr size_t N = vector_i8::SIZE;
   15|  2.28k|  constexpr size_t max_iterations = 255 / 2;
   16|       |
   17|  2.28k|  auto counters = vector_u64::zero();
   18|  2.28k|  auto local = vector_u8::zero();
   19|       |
   20|  2.28k|  size_t iterations = 0;
   21|  2.28k|  size_t pos = 0;
   22|  2.28k|  size_t count = 0;
   23|  3.61M|  for (; pos + N <= size; pos += N) {
  ------------------
  |  Branch (23:10): [True: 3.61M, False: 2.28k]
  ------------------
   24|  3.61M|    const auto input =
   25|  3.61M|        vector_i8::load(reinterpret_cast<const int8_t *>(in + pos));
   26|       |
   27|  3.61M|    const auto continuation = input > int8_t(-65);
   28|  3.61M|    const auto utf_4bytes = vector_u8(input.value) >= uint8_t(240);
   29|       |
   30|  3.61M|    local -= vector_u8(continuation);
   31|  3.61M|    local -= vector_u8(utf_4bytes);
   32|       |
   33|  3.61M|    iterations += 1;
   34|  3.61M|    if (iterations == max_iterations) {
  ------------------
  |  Branch (34:9): [True: 28.2k, False: 3.58M]
  ------------------
   35|  28.2k|      counters += sum_8bytes(local);
   36|  28.2k|      local = vector_u8::zero();
   37|  28.2k|      iterations = 0;
   38|  28.2k|    }
   39|  3.61M|  }
   40|       |
   41|  2.28k|  if (iterations > 0) {
  ------------------
  |  Branch (41:7): [True: 1.63k, False: 658]
  ------------------
   42|  1.63k|    count += local.sum_bytes();
   43|  1.63k|  }
   44|       |
   45|  2.28k|  count += counters.sum();
   46|       |
   47|  2.28k|  return count + scalar::utf8::utf16_length_from_utf8(in + pos, size - pos);
   48|  2.28k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14utf831utf16_length_from_utf8_bytemaskEPKcm:
    9|  2.28k|                                                             size_t size) {
   10|  2.28k|  using vector_i8 = simd8<int8_t>;
   11|  2.28k|  using vector_u8 = simd8<uint8_t>;
   12|  2.28k|  using vector_u64 = simd64<uint64_t>;
   13|       |
   14|  2.28k|  constexpr size_t N = vector_i8::SIZE;
   15|  2.28k|  constexpr size_t max_iterations = 255 / 2;
   16|       |
   17|  2.28k|  auto counters = vector_u64::zero();
   18|  2.28k|  auto local = vector_u8::zero();
   19|       |
   20|  2.28k|  size_t iterations = 0;
   21|  2.28k|  size_t pos = 0;
   22|  2.28k|  size_t count = 0;
   23|  7.22M|  for (; pos + N <= size; pos += N) {
  ------------------
  |  Branch (23:10): [True: 7.22M, False: 2.28k]
  ------------------
   24|  7.22M|    const auto input =
   25|  7.22M|        vector_i8::load(reinterpret_cast<const int8_t *>(in + pos));
   26|       |
   27|  7.22M|    const auto continuation = input > int8_t(-65);
   28|  7.22M|    const auto utf_4bytes = vector_u8(input.value) >= uint8_t(240);
   29|       |
   30|  7.22M|    local -= vector_u8(continuation);
   31|  7.22M|    local -= vector_u8(utf_4bytes);
   32|       |
   33|  7.22M|    iterations += 1;
   34|  7.22M|    if (iterations == max_iterations) {
  ------------------
  |  Branch (34:9): [True: 56.6k, False: 7.16M]
  ------------------
   35|  56.6k|      counters += sum_8bytes(local);
   36|  56.6k|      local = vector_u8::zero();
   37|  56.6k|      iterations = 0;
   38|  56.6k|    }
   39|  7.22M|  }
   40|       |
   41|  2.28k|  if (iterations > 0) {
  ------------------
  |  Branch (41:7): [True: 1.74k, False: 545]
  ------------------
   42|  1.74k|    count += local.sum_bytes();
   43|  1.74k|  }
   44|       |
   45|  2.28k|  count += counters.sum();
   46|       |
   47|  2.28k|  return count + scalar::utf8::utf16_length_from_utf8(in + pos, size - pos);
   48|  2.28k|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_114utf8_to_latin121validating_transcoderC2Ev:
  101|  1.44k|  validating_transcoder() : error(uint8_t(0)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_114utf8_to_latin121validating_transcoder7convertEPKcmPc:
  147|    710|                                       char *latin1_output) {
  148|    710|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (148:9): [True: 174, False: 536]
  ------------------
  149|    174|      return scalar::utf8_to_latin1::convert(in, size, latin1_output);
  150|    174|    }
  151|    536|    size_t pos = 0;
  152|    536|    char *start{latin1_output};
  153|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  154|       |    // of 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the
  155|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  156|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  157|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  158|       |    // back from the end counting 16 leading bytes, to give us a good margin.
  159|    536|    size_t leading_byte = 0;
  160|    536|    size_t margin = size;
  161|  11.6k|    for (; margin > 0 && leading_byte < 16; margin--) {
  ------------------
  |  Branch (161:12): [True: 11.6k, False: 14]
  |  Branch (161:26): [True: 11.1k, False: 522]
  ------------------
  162|  11.1k|      leading_byte += (int8_t(in[margin - 1]) >
  163|  11.1k|                       -65); // twos complement of -65 is 1011 1111 ...
  164|  11.1k|    }
  165|       |    // If the input is long enough, then we have that margin-1 is the eight last
  166|       |    // leading byte.
  167|    536|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  168|   494k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (168:12): [True: 493k, False: 504]
  ------------------
  169|   493k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  170|   493k|      if (input.is_ascii()) {
  ------------------
  |  Branch (170:11): [True: 476k, False: 16.8k]
  ------------------
  171|   476k|        input.store((int8_t *)latin1_output);
  172|   476k|        latin1_output += 64;
  173|   476k|        pos += 64;
  174|   476k|      } else {
  175|       |        // you might think that a for-loop would work, but under Visual Studio,
  176|       |        // it is not good enough.
  177|  16.8k|        static_assert(
  178|  16.8k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  179|  16.8k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  180|  16.8k|            "We support either two or four chunks per 64-byte block.");
  181|  16.8k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  182|  16.8k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  183|  16.8k|          this->check_utf8_bytes(input.chunks[0], zero);
  184|  16.8k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  185|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  186|       |          this->check_utf8_bytes(input.chunks[0], zero);
  187|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  188|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  189|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  190|       |        }
  191|  16.8k|        uint64_t utf8_continuation_mask =
  192|  16.8k|            input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in
  193|       |                               // this case, we also have ASCII to account for.
  194|  16.8k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (194:13): [True: 32, False: 16.8k]
  ------------------
  195|     32|          return 0; // error
  196|     32|        }
  197|  16.8k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  198|  16.8k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  199|       |        // We process in blocks of up to 12 bytes except possibly
  200|       |        // for fast paths which may process up to 16 bytes. For the
  201|       |        // slow path to work, we should have at least 12 input bytes left.
  202|  16.8k|        size_t max_starting_point = (pos + 64) - 12;
  203|       |        // Next loop is going to run at least five times.
  204|   165k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (204:16): [True: 148k, False: 16.8k]
  ------------------
  205|       |          // Performance note: our ability to compute 'consumed' and
  206|       |          // then shift and recompute is critical. If there is a
  207|       |          // latency of, say, 4 cycles on getting 'consumed', then
  208|       |          // the inner loop might have a total latency of about 6 cycles.
  209|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  210|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  211|       |          // for this section of the code. Hence, there is a limit
  212|       |          // to how much we can further increase this latency before
  213|       |          // it seriously harms performance.
  214|   148k|          size_t consumed = convert_masked_utf8_to_latin1(
  215|   148k|              in + pos, utf8_end_of_code_point_mask, latin1_output);
  216|   148k|          pos += consumed;
  217|   148k|          utf8_end_of_code_point_mask >>= consumed;
  218|   148k|        }
  219|       |        // At this point there may remain between 0 and 12 bytes in the
  220|       |        // 64-byte block. These bytes will be processed again. So we have an
  221|       |        // 80% efficiency (in the worst case). In practice we expect an
  222|       |        // 85% to 90% efficiency.
  223|  16.8k|      }
  224|   493k|    }
  225|    504|    if (errors()) {
  ------------------
  |  Branch (225:9): [True: 178, False: 326]
  ------------------
  226|    178|      return 0;
  227|    178|    }
  228|    326|    const size_t ascii = convert_ascii_tail(in, pos, size, latin1_output);
  229|    326|    pos += ascii;
  230|    326|    latin1_output += ascii;
  231|    326|    if (pos == size) {
  ------------------
  |  Branch (231:9): [True: 90, False: 236]
  ------------------
  232|     90|      return latin1_output - start;
  233|     90|    }
  234|    236|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|    236|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
  235|    236|    size_t howmany =
  236|    236|        scalar::utf8_to_latin1::convert(in + pos, size - pos, latin1_output);
  237|    236|    if (howmany == 0) {
  ------------------
  |  Branch (237:9): [True: 190, False: 46]
  ------------------
  238|    190|      return 0;
  239|    190|    }
  240|     46|    return latin1_output + howmany - start;
  241|    236|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_114utf8_to_latin121validating_transcoder16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  106|  68.3k|                                              const simd8<uint8_t> prev_input) {
  107|       |    // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+
  108|       |    // lead bytes (2, 3, 4-byte leads become large positive numbers instead of
  109|       |    // small negative numbers)
  110|  68.3k|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  111|  68.3k|    this->error |= check_special_cases(input, prev1);
  112|  68.3k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_114utf8_to_latin119check_special_casesENS1_4simd5simd8IhEES5_:
    8|  68.3k|check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
    9|       |  // For UTF-8 to Latin 1, we can allow any ASCII character, and any
   10|       |  // continuation byte, but the non-ASCII leading bytes must be 0b11000011 or
   11|       |  // 0b11000010 and nothing else.
   12|       |  //
   13|       |  // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
   14|       |  // Bit 1 = Too Long (ASCII followed by continuation)
   15|       |  // Bit 2 = Overlong 3-byte
   16|       |  // Bit 4 = Surrogate
   17|       |  // Bit 5 = Overlong 2-byte
   18|       |  // Bit 7 = Two Continuations
   19|  68.3k|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   20|       |                                               // 11______ 11______
   21|  68.3k|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   22|  68.3k|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   23|  68.3k|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   24|  68.3k|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   25|  68.3k|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   26|  68.3k|  constexpr const uint8_t TOO_LARGE = 1 << 3;  // 11110100 1001____
   27|       |                                               // 11110100 101_____
   28|       |                                               // 11110101 1001____
   29|       |                                               // 11110101 101_____
   30|       |                                               // 1111011_ 1001____
   31|       |                                               // 1111011_ 101_____
   32|       |                                               // 11111___ 1001____
   33|       |                                               // 11111___ 101_____
   34|  68.3k|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   35|       |  // 11110101 1000____
   36|       |  // 1111011_ 1000____
   37|       |  // 11111___ 1000____
   38|  68.3k|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   39|  68.3k|  constexpr const uint8_t FORBIDDEN = 0xff;
   40|       |
   41|  68.3k|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   42|       |      // 0_______ ________ <ASCII in byte 1>
   43|  68.3k|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   44|  68.3k|      TOO_LONG,
   45|       |      // 10______ ________ <continuation in byte 1>
   46|  68.3k|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   47|       |      // 1100____ ________ <two byte lead in byte 1>
   48|  68.3k|      TOO_SHORT | OVERLONG_2,
   49|       |      // 1101____ ________ <two byte lead in byte 1>
   50|  68.3k|      FORBIDDEN,
   51|       |      // 1110____ ________ <three byte lead in byte 1>
   52|  68.3k|      FORBIDDEN,
   53|       |      // 1111____ ________ <four+ byte lead in byte 1>
   54|  68.3k|      FORBIDDEN);
   55|  68.3k|  constexpr const uint8_t CARRY =
   56|  68.3k|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   57|  68.3k|  const simd8<uint8_t> byte_1_low =
   58|  68.3k|      (prev1 & 0x0F)
   59|  68.3k|          .lookup_16<uint8_t>(
   60|       |              // ____0000 ________
   61|  68.3k|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   62|       |              // ____0001 ________
   63|  68.3k|              CARRY | OVERLONG_2,
   64|       |              // ____001_ ________
   65|  68.3k|              CARRY, CARRY,
   66|       |
   67|       |              // ____0100 ________
   68|  68.3k|              FORBIDDEN,
   69|       |              // ____0101 ________
   70|  68.3k|              FORBIDDEN,
   71|       |              // ____011_ ________
   72|  68.3k|              FORBIDDEN, FORBIDDEN,
   73|       |
   74|       |              // ____1___ ________
   75|  68.3k|              FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN,
   76|       |              // ____1101 ________
   77|  68.3k|              FORBIDDEN, FORBIDDEN, FORBIDDEN);
   78|  68.3k|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   79|       |      // ________ 0_______ <ASCII in byte 2>
   80|  68.3k|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   81|  68.3k|      TOO_SHORT, TOO_SHORT,
   82|       |
   83|       |      // ________ 1000____
   84|  68.3k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   85|  68.3k|          OVERLONG_4,
   86|       |      // ________ 1001____
   87|  68.3k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   88|       |      // ________ 101_____
   89|  68.3k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   90|  68.3k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   91|       |
   92|       |      // ________ 11______
   93|  68.3k|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   94|  68.3k|  return (byte_1_high & byte_1_low & byte_2_high);
   95|  68.3k|}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_114utf8_to_latin121validating_transcoder6errorsEv:
  348|  17.7k|  simdutf_really_inline bool errors() const {
  349|  17.7k|    return this->error.any_bits_set_anywhere();
  350|  17.7k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_114utf8_to_latin121validating_transcoder18convert_ascii_tailEPKcmmPc:
  115|    734|                                                  size_t size, char *output) {
  116|       |    // A cheap test on the first bytes avoids any SIMD work for most non-ASCII
  117|       |    // tails.
  118|    734|    if (size - pos >= 8) {
  ------------------
  |  Branch (118:9): [True: 734, False: 0]
  ------------------
  119|    734|      uint64_t first_bytes;
  120|    734|      std::memcpy(&first_bytes, in + pos, 8);
  121|    734|      if (first_bytes & 0x8080808080808080) {
  ------------------
  |  Branch (121:11): [True: 146, False: 588]
  ------------------
  122|    146|        return 0;
  123|    146|      }
  124|    734|    }
  125|    588|    const size_t start = pos;
  126|    718|    while (pos + 64 <= size) {
  ------------------
  |  Branch (126:12): [True: 296, False: 422]
  ------------------
  127|    296|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  128|    296|      if (!input.is_ascii()) {
  ------------------
  |  Branch (128:11): [True: 166, False: 130]
  ------------------
  129|    166|        return pos - start;
  130|    166|      }
  131|    130|      input.store(reinterpret_cast<int8_t *>(output + (pos - start)));
  132|    130|      pos += 64;
  133|    130|    }
  134|    422|    if (pos == size || size < 64) {
  ------------------
  |  Branch (134:9): [True: 38, False: 384]
  |  Branch (134:24): [True: 0, False: 384]
  ------------------
  135|     38|      return pos - start;
  136|     38|    }
  137|    384|    simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + size - 64));
  138|    384|    if (!input.is_ascii()) {
  ------------------
  |  Branch (138:9): [True: 250, False: 134]
  ------------------
  139|    250|      return pos - start;
  140|    250|    }
  141|    134|    input.store(reinterpret_cast<int8_t *>(output + (pos - start) -
  142|    134|                                           (pos - (size - 64))));
  143|    134|    return size - start;
  144|    384|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_114utf8_to_latin121validating_transcoder19convert_with_errorsEPKcmPc:
  244|    730|                                                   char *latin1_output) {
  245|    730|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (245:9): [True: 184, False: 546]
  ------------------
  246|    184|      return scalar::utf8_to_latin1::convert_with_errors(in, size,
  247|    184|                                                         latin1_output);
  248|    184|    }
  249|    546|    size_t pos = 0;
  250|    546|    char *start{latin1_output};
  251|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  252|       |    // of 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the
  253|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  254|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  255|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  256|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  257|    546|    size_t leading_byte = 0;
  258|    546|    size_t margin = size;
  259|  9.98k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (259:12): [True: 9.96k, False: 14]
  |  Branch (259:26): [True: 9.43k, False: 532]
  ------------------
  260|  9.43k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  261|  9.43k|    }
  262|       |    // If the input is long enough, then we have that margin-1 is the eight last
  263|       |    // leading byte.
  264|    546|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  265|   387k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (265:12): [True: 387k, False: 408]
  ------------------
  266|   387k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  267|   387k|      if (input.is_ascii()) {
  ------------------
  |  Branch (267:11): [True: 370k, False: 17.2k]
  ------------------
  268|   370k|        input.store((int8_t *)latin1_output);
  269|   370k|        latin1_output += 64;
  270|   370k|        pos += 64;
  271|   370k|      } else {
  272|       |        // you might think that a for-loop would work, but under Visual Studio,
  273|       |        // it is not good enough.
  274|  17.2k|        static_assert(
  275|  17.2k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  276|  17.2k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  277|  17.2k|            "We support either two or four chunks per 64-byte block.");
  278|  17.2k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  279|  17.2k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  280|  17.2k|          this->check_utf8_bytes(input.chunks[0], zero);
  281|  17.2k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  282|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  283|       |          this->check_utf8_bytes(input.chunks[0], zero);
  284|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  285|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  286|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  287|       |        }
  288|  17.2k|        if (errors()) {
  ------------------
  |  Branch (288:13): [True: 138, False: 17.1k]
  ------------------
  289|       |          // rewind_and_convert_with_errors will seek a potential error from
  290|       |          // in+pos onward, with the ability to go back up to pos bytes, and
  291|       |          // read size-pos bytes forward.
  292|    138|          SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|    138|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
  293|    138|          result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(
  294|    138|              pos, in + pos, size - pos, latin1_output);
  295|    138|          res.count += pos;
  296|    138|          return res;
  297|    138|        }
  298|  17.1k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  299|  17.1k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  300|  17.1k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  301|       |        // We process in blocks of up to 12 bytes except possibly
  302|       |        // for fast paths which may process up to 16 bytes. For the
  303|       |        // slow path to work, we should have at least 12 input bytes left.
  304|  17.1k|        size_t max_starting_point = (pos + 64) - 12;
  305|       |        // Next loop is going to run at least five times.
  306|   170k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (306:16): [True: 153k, False: 17.1k]
  ------------------
  307|       |          // Performance note: our ability to compute 'consumed' and
  308|       |          // then shift and recompute is critical. If there is a
  309|       |          // latency of, say, 4 cycles on getting 'consumed', then
  310|       |          // the inner loop might have a total latency of about 6 cycles.
  311|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  312|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  313|       |          // for this section of the code. Hence, there is a limit
  314|       |          // to how much we can further increase this latency before
  315|       |          // it seriously harms performance.
  316|   153k|          size_t consumed = convert_masked_utf8_to_latin1(
  317|   153k|              in + pos, utf8_end_of_code_point_mask, latin1_output);
  318|   153k|          pos += consumed;
  319|   153k|          utf8_end_of_code_point_mask >>= consumed;
  320|   153k|        }
  321|       |        // At this point there may remain between 0 and 12 bytes in the
  322|       |        // 64-byte block. These bytes will be processed again. So we have an
  323|       |        // 80% efficiency (in the worst case). In practice we expect an
  324|       |        // 85% to 90% efficiency.
  325|  17.1k|      }
  326|   387k|    }
  327|    408|    const size_t ascii = convert_ascii_tail(in, pos, size, latin1_output);
  328|    408|    pos += ascii;
  329|    408|    latin1_output += ascii;
  330|    408|    if (pos == size) {
  ------------------
  |  Branch (330:9): [True: 82, False: 326]
  ------------------
  331|     82|      return result(error_code::SUCCESS, latin1_output - start);
  332|     82|    }
  333|       |    // rewind_and_convert_with_errors will seek a potential error from in+pos
  334|       |    // onward, with the ability to go back up to pos bytes, and read size-pos
  335|       |    // bytes forward.
  336|    326|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|    326|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
  337|    326|    result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(
  338|    326|        pos, in + pos, size - pos, latin1_output);
  339|    326|    if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (339:9): [True: 222, False: 104]
  ------------------
  340|    222|      res.count += pos;
  341|    222|      return res;
  342|    222|    }
  343|       |    // In case of success, we want the number of words written
  344|    104|    latin1_output += res.count;
  345|    104|    return result(error_code::SUCCESS, latin1_output - start);
  346|    326|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_114utf8_to_latin121validating_transcoderC2Ev:
  101|  1.44k|  validating_transcoder() : error(uint8_t(0)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_114utf8_to_latin121validating_transcoder7convertEPKcmPc:
  147|    710|                                       char *latin1_output) {
  148|    710|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (148:9): [True: 174, False: 536]
  ------------------
  149|    174|      return scalar::utf8_to_latin1::convert(in, size, latin1_output);
  150|    174|    }
  151|    536|    size_t pos = 0;
  152|    536|    char *start{latin1_output};
  153|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  154|       |    // of 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the
  155|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  156|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  157|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  158|       |    // back from the end counting 16 leading bytes, to give us a good margin.
  159|    536|    size_t leading_byte = 0;
  160|    536|    size_t margin = size;
  161|  11.6k|    for (; margin > 0 && leading_byte < 16; margin--) {
  ------------------
  |  Branch (161:12): [True: 11.6k, False: 14]
  |  Branch (161:26): [True: 11.1k, False: 522]
  ------------------
  162|  11.1k|      leading_byte += (int8_t(in[margin - 1]) >
  163|  11.1k|                       -65); // twos complement of -65 is 1011 1111 ...
  164|  11.1k|    }
  165|       |    // If the input is long enough, then we have that margin-1 is the eight last
  166|       |    // leading byte.
  167|    536|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  168|   494k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (168:12): [True: 493k, False: 504]
  ------------------
  169|   493k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  170|   493k|      if (input.is_ascii()) {
  ------------------
  |  Branch (170:11): [True: 476k, False: 16.8k]
  ------------------
  171|   476k|        input.store((int8_t *)latin1_output);
  172|   476k|        latin1_output += 64;
  173|   476k|        pos += 64;
  174|   476k|      } else {
  175|       |        // you might think that a for-loop would work, but under Visual Studio,
  176|       |        // it is not good enough.
  177|  16.8k|        static_assert(
  178|  16.8k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  179|  16.8k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  180|  16.8k|            "We support either two or four chunks per 64-byte block.");
  181|  16.8k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  182|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  183|       |          this->check_utf8_bytes(input.chunks[0], zero);
  184|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  185|  16.8k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  186|  16.8k|          this->check_utf8_bytes(input.chunks[0], zero);
  187|  16.8k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  188|  16.8k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  189|  16.8k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  190|  16.8k|        }
  191|  16.8k|        uint64_t utf8_continuation_mask =
  192|  16.8k|            input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in
  193|       |                               // this case, we also have ASCII to account for.
  194|  16.8k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (194:13): [True: 32, False: 16.8k]
  ------------------
  195|     32|          return 0; // error
  196|     32|        }
  197|  16.8k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  198|  16.8k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  199|       |        // We process in blocks of up to 12 bytes except possibly
  200|       |        // for fast paths which may process up to 16 bytes. For the
  201|       |        // slow path to work, we should have at least 12 input bytes left.
  202|  16.8k|        size_t max_starting_point = (pos + 64) - 12;
  203|       |        // Next loop is going to run at least five times.
  204|   165k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (204:16): [True: 148k, False: 16.8k]
  ------------------
  205|       |          // Performance note: our ability to compute 'consumed' and
  206|       |          // then shift and recompute is critical. If there is a
  207|       |          // latency of, say, 4 cycles on getting 'consumed', then
  208|       |          // the inner loop might have a total latency of about 6 cycles.
  209|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  210|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  211|       |          // for this section of the code. Hence, there is a limit
  212|       |          // to how much we can further increase this latency before
  213|       |          // it seriously harms performance.
  214|   148k|          size_t consumed = convert_masked_utf8_to_latin1(
  215|   148k|              in + pos, utf8_end_of_code_point_mask, latin1_output);
  216|   148k|          pos += consumed;
  217|   148k|          utf8_end_of_code_point_mask >>= consumed;
  218|   148k|        }
  219|       |        // At this point there may remain between 0 and 12 bytes in the
  220|       |        // 64-byte block. These bytes will be processed again. So we have an
  221|       |        // 80% efficiency (in the worst case). In practice we expect an
  222|       |        // 85% to 90% efficiency.
  223|  16.8k|      }
  224|   493k|    }
  225|    504|    if (errors()) {
  ------------------
  |  Branch (225:9): [True: 178, False: 326]
  ------------------
  226|    178|      return 0;
  227|    178|    }
  228|    326|    const size_t ascii = convert_ascii_tail(in, pos, size, latin1_output);
  229|    326|    pos += ascii;
  230|    326|    latin1_output += ascii;
  231|    326|    if (pos == size) {
  ------------------
  |  Branch (231:9): [True: 90, False: 236]
  ------------------
  232|     90|      return latin1_output - start;
  233|     90|    }
  234|    236|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   16|    236|#define SIMDUTF_VZEROUPPER() (void)0
  ------------------
  235|    236|    size_t howmany =
  236|    236|        scalar::utf8_to_latin1::convert(in + pos, size - pos, latin1_output);
  237|    236|    if (howmany == 0) {
  ------------------
  |  Branch (237:9): [True: 190, False: 46]
  ------------------
  238|    190|      return 0;
  239|    190|    }
  240|     46|    return latin1_output + howmany - start;
  241|    236|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_114utf8_to_latin121validating_transcoder16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  106|   136k|                                              const simd8<uint8_t> prev_input) {
  107|       |    // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+
  108|       |    // lead bytes (2, 3, 4-byte leads become large positive numbers instead of
  109|       |    // small negative numbers)
  110|   136k|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  111|   136k|    this->error |= check_special_cases(input, prev1);
  112|   136k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_114utf8_to_latin119check_special_casesENS1_4simd5simd8IhEES5_:
    8|   136k|check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
    9|       |  // For UTF-8 to Latin 1, we can allow any ASCII character, and any
   10|       |  // continuation byte, but the non-ASCII leading bytes must be 0b11000011 or
   11|       |  // 0b11000010 and nothing else.
   12|       |  //
   13|       |  // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
   14|       |  // Bit 1 = Too Long (ASCII followed by continuation)
   15|       |  // Bit 2 = Overlong 3-byte
   16|       |  // Bit 4 = Surrogate
   17|       |  // Bit 5 = Overlong 2-byte
   18|       |  // Bit 7 = Two Continuations
   19|   136k|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   20|       |                                               // 11______ 11______
   21|   136k|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   22|   136k|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   23|   136k|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   24|   136k|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   25|   136k|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   26|   136k|  constexpr const uint8_t TOO_LARGE = 1 << 3;  // 11110100 1001____
   27|       |                                               // 11110100 101_____
   28|       |                                               // 11110101 1001____
   29|       |                                               // 11110101 101_____
   30|       |                                               // 1111011_ 1001____
   31|       |                                               // 1111011_ 101_____
   32|       |                                               // 11111___ 1001____
   33|       |                                               // 11111___ 101_____
   34|   136k|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   35|       |  // 11110101 1000____
   36|       |  // 1111011_ 1000____
   37|       |  // 11111___ 1000____
   38|   136k|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   39|   136k|  constexpr const uint8_t FORBIDDEN = 0xff;
   40|       |
   41|   136k|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   42|       |      // 0_______ ________ <ASCII in byte 1>
   43|   136k|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   44|   136k|      TOO_LONG,
   45|       |      // 10______ ________ <continuation in byte 1>
   46|   136k|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   47|       |      // 1100____ ________ <two byte lead in byte 1>
   48|   136k|      TOO_SHORT | OVERLONG_2,
   49|       |      // 1101____ ________ <two byte lead in byte 1>
   50|   136k|      FORBIDDEN,
   51|       |      // 1110____ ________ <three byte lead in byte 1>
   52|   136k|      FORBIDDEN,
   53|       |      // 1111____ ________ <four+ byte lead in byte 1>
   54|   136k|      FORBIDDEN);
   55|   136k|  constexpr const uint8_t CARRY =
   56|   136k|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   57|   136k|  const simd8<uint8_t> byte_1_low =
   58|   136k|      (prev1 & 0x0F)
   59|   136k|          .lookup_16<uint8_t>(
   60|       |              // ____0000 ________
   61|   136k|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   62|       |              // ____0001 ________
   63|   136k|              CARRY | OVERLONG_2,
   64|       |              // ____001_ ________
   65|   136k|              CARRY, CARRY,
   66|       |
   67|       |              // ____0100 ________
   68|   136k|              FORBIDDEN,
   69|       |              // ____0101 ________
   70|   136k|              FORBIDDEN,
   71|       |              // ____011_ ________
   72|   136k|              FORBIDDEN, FORBIDDEN,
   73|       |
   74|       |              // ____1___ ________
   75|   136k|              FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN,
   76|       |              // ____1101 ________
   77|   136k|              FORBIDDEN, FORBIDDEN, FORBIDDEN);
   78|   136k|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   79|       |      // ________ 0_______ <ASCII in byte 2>
   80|   136k|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   81|   136k|      TOO_SHORT, TOO_SHORT,
   82|       |
   83|       |      // ________ 1000____
   84|   136k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   85|   136k|          OVERLONG_4,
   86|       |      // ________ 1001____
   87|   136k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   88|       |      // ________ 101_____
   89|   136k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   90|   136k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   91|       |
   92|       |      // ________ 11______
   93|   136k|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   94|   136k|  return (byte_1_high & byte_1_low & byte_2_high);
   95|   136k|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_114utf8_to_latin121validating_transcoder6errorsEv:
  348|  17.7k|  simdutf_really_inline bool errors() const {
  349|  17.7k|    return this->error.any_bits_set_anywhere();
  350|  17.7k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_114utf8_to_latin121validating_transcoder18convert_ascii_tailEPKcmmPc:
  115|    734|                                                  size_t size, char *output) {
  116|       |    // A cheap test on the first bytes avoids any SIMD work for most non-ASCII
  117|       |    // tails.
  118|    734|    if (size - pos >= 8) {
  ------------------
  |  Branch (118:9): [True: 734, False: 0]
  ------------------
  119|    734|      uint64_t first_bytes;
  120|    734|      std::memcpy(&first_bytes, in + pos, 8);
  121|    734|      if (first_bytes & 0x8080808080808080) {
  ------------------
  |  Branch (121:11): [True: 146, False: 588]
  ------------------
  122|    146|        return 0;
  123|    146|      }
  124|    734|    }
  125|    588|    const size_t start = pos;
  126|    718|    while (pos + 64 <= size) {
  ------------------
  |  Branch (126:12): [True: 296, False: 422]
  ------------------
  127|    296|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  128|    296|      if (!input.is_ascii()) {
  ------------------
  |  Branch (128:11): [True: 166, False: 130]
  ------------------
  129|    166|        return pos - start;
  130|    166|      }
  131|    130|      input.store(reinterpret_cast<int8_t *>(output + (pos - start)));
  132|    130|      pos += 64;
  133|    130|    }
  134|    422|    if (pos == size || size < 64) {
  ------------------
  |  Branch (134:9): [True: 38, False: 384]
  |  Branch (134:24): [True: 0, False: 384]
  ------------------
  135|     38|      return pos - start;
  136|     38|    }
  137|    384|    simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + size - 64));
  138|    384|    if (!input.is_ascii()) {
  ------------------
  |  Branch (138:9): [True: 250, False: 134]
  ------------------
  139|    250|      return pos - start;
  140|    250|    }
  141|    134|    input.store(reinterpret_cast<int8_t *>(output + (pos - start) -
  142|    134|                                           (pos - (size - 64))));
  143|    134|    return size - start;
  144|    384|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_114utf8_to_latin121validating_transcoder19convert_with_errorsEPKcmPc:
  244|    730|                                                   char *latin1_output) {
  245|    730|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (245:9): [True: 184, False: 546]
  ------------------
  246|    184|      return scalar::utf8_to_latin1::convert_with_errors(in, size,
  247|    184|                                                         latin1_output);
  248|    184|    }
  249|    546|    size_t pos = 0;
  250|    546|    char *start{latin1_output};
  251|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  252|       |    // of 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the
  253|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  254|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  255|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  256|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  257|    546|    size_t leading_byte = 0;
  258|    546|    size_t margin = size;
  259|  9.98k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (259:12): [True: 9.96k, False: 14]
  |  Branch (259:26): [True: 9.43k, False: 532]
  ------------------
  260|  9.43k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  261|  9.43k|    }
  262|       |    // If the input is long enough, then we have that margin-1 is the eight last
  263|       |    // leading byte.
  264|    546|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  265|   387k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (265:12): [True: 387k, False: 408]
  ------------------
  266|   387k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  267|   387k|      if (input.is_ascii()) {
  ------------------
  |  Branch (267:11): [True: 370k, False: 17.2k]
  ------------------
  268|   370k|        input.store((int8_t *)latin1_output);
  269|   370k|        latin1_output += 64;
  270|   370k|        pos += 64;
  271|   370k|      } else {
  272|       |        // you might think that a for-loop would work, but under Visual Studio,
  273|       |        // it is not good enough.
  274|  17.2k|        static_assert(
  275|  17.2k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  276|  17.2k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  277|  17.2k|            "We support either two or four chunks per 64-byte block.");
  278|  17.2k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  279|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  280|       |          this->check_utf8_bytes(input.chunks[0], zero);
  281|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  282|  17.2k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  283|  17.2k|          this->check_utf8_bytes(input.chunks[0], zero);
  284|  17.2k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  285|  17.2k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  286|  17.2k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  287|  17.2k|        }
  288|  17.2k|        if (errors()) {
  ------------------
  |  Branch (288:13): [True: 138, False: 17.1k]
  ------------------
  289|       |          // rewind_and_convert_with_errors will seek a potential error from
  290|       |          // in+pos onward, with the ability to go back up to pos bytes, and
  291|       |          // read size-pos bytes forward.
  292|    138|          SIMDUTF_VZEROUPPER();
  ------------------
  |  |   16|    138|#define SIMDUTF_VZEROUPPER() (void)0
  ------------------
  293|    138|          result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(
  294|    138|              pos, in + pos, size - pos, latin1_output);
  295|    138|          res.count += pos;
  296|    138|          return res;
  297|    138|        }
  298|  17.1k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  299|  17.1k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  300|  17.1k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  301|       |        // We process in blocks of up to 12 bytes except possibly
  302|       |        // for fast paths which may process up to 16 bytes. For the
  303|       |        // slow path to work, we should have at least 12 input bytes left.
  304|  17.1k|        size_t max_starting_point = (pos + 64) - 12;
  305|       |        // Next loop is going to run at least five times.
  306|   170k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (306:16): [True: 153k, False: 17.1k]
  ------------------
  307|       |          // Performance note: our ability to compute 'consumed' and
  308|       |          // then shift and recompute is critical. If there is a
  309|       |          // latency of, say, 4 cycles on getting 'consumed', then
  310|       |          // the inner loop might have a total latency of about 6 cycles.
  311|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  312|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  313|       |          // for this section of the code. Hence, there is a limit
  314|       |          // to how much we can further increase this latency before
  315|       |          // it seriously harms performance.
  316|   153k|          size_t consumed = convert_masked_utf8_to_latin1(
  317|   153k|              in + pos, utf8_end_of_code_point_mask, latin1_output);
  318|   153k|          pos += consumed;
  319|   153k|          utf8_end_of_code_point_mask >>= consumed;
  320|   153k|        }
  321|       |        // At this point there may remain between 0 and 12 bytes in the
  322|       |        // 64-byte block. These bytes will be processed again. So we have an
  323|       |        // 80% efficiency (in the worst case). In practice we expect an
  324|       |        // 85% to 90% efficiency.
  325|  17.1k|      }
  326|   387k|    }
  327|    408|    const size_t ascii = convert_ascii_tail(in, pos, size, latin1_output);
  328|    408|    pos += ascii;
  329|    408|    latin1_output += ascii;
  330|    408|    if (pos == size) {
  ------------------
  |  Branch (330:9): [True: 82, False: 326]
  ------------------
  331|     82|      return result(error_code::SUCCESS, latin1_output - start);
  332|     82|    }
  333|       |    // rewind_and_convert_with_errors will seek a potential error from in+pos
  334|       |    // onward, with the ability to go back up to pos bytes, and read size-pos
  335|       |    // bytes forward.
  336|    326|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   16|    326|#define SIMDUTF_VZEROUPPER() (void)0
  ------------------
  337|    326|    result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(
  338|    326|        pos, in + pos, size - pos, latin1_output);
  339|    326|    if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (339:9): [True: 222, False: 104]
  ------------------
  340|    222|      res.count += pos;
  341|    222|      return res;
  342|    222|    }
  343|       |    // In case of success, we want the number of words written
  344|    104|    latin1_output += res.count;
  345|    104|    return result(error_code::SUCCESS, latin1_output - start);
  346|    326|  }

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoderC2Ev:
  114|  3.63k|  validating_transcoder() : error(uint8_t(0)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoderC2Ev:
  114|  3.63k|  validating_transcoder() : error(uint8_t(0)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoder7convertILNS_10endiannessE0EEEmPKcmPDs:
  164|    950|                                       char16_t *utf16_output) {
  165|    950|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (165:9): [True: 294, False: 656]
  ------------------
  166|    294|      return scalar::utf8_to_utf16::convert<endian>(in, size, utf16_output);
  167|    294|    }
  168|    656|    size_t pos = 0;
  169|    656|    char16_t *start{utf16_output};
  170|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  171|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  172|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  173|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  174|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  175|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  176|    656|    size_t leading_byte = 0;
  177|    656|    size_t margin = size;
  178|  9.10k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (178:12): [True: 9.08k, False: 16]
  |  Branch (178:26): [True: 8.44k, False: 640]
  ------------------
  179|  8.44k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  180|  8.44k|    }
  181|       |    // If the input is long enough, then we have that margin-1 is the eight last
  182|       |    // leading byte.
  183|    656|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  184|   668k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (184:12): [True: 667k, False: 626]
  ------------------
  185|   667k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  186|   667k|      if (input.is_ascii()) {
  ------------------
  |  Branch (186:11): [True: 571k, False: 95.8k]
  ------------------
  187|   571k|        input.store_ascii_as_utf16<endian>(utf16_output);
  188|   571k|        utf16_output += 64;
  189|   571k|        pos += 64;
  190|   571k|      } else {
  191|       |        // you might think that a for-loop would work, but under Visual Studio,
  192|       |        // it is not good enough.
  193|  95.8k|        static_assert(
  194|  95.8k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  195|  95.8k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  196|  95.8k|            "We support either two or four chunks per 64-byte block.");
  197|  95.8k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  198|  95.8k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  199|  95.8k|          this->check_utf8_bytes(input.chunks[0], zero);
  200|  95.8k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  201|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  202|       |          this->check_utf8_bytes(input.chunks[0], zero);
  203|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  204|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  205|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  206|       |        }
  207|  95.8k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  208|  95.8k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (208:13): [True: 30, False: 95.8k]
  ------------------
  209|     30|          return 0; // error
  210|     30|        }
  211|  95.8k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  212|  95.8k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  213|       |        // We process in blocks of up to 12 bytes except possibly
  214|       |        // for fast paths which may process up to 16 bytes. For the
  215|       |        // slow path to work, we should have at least 12 input bytes left.
  216|  95.8k|        size_t max_starting_point = (pos + 64) - 12;
  217|       |        // Next loop is going to run at least five times.
  218|   938k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (218:16): [True: 842k, False: 95.8k]
  ------------------
  219|       |          // Performance note: our ability to compute 'consumed' and
  220|       |          // then shift and recompute is critical. If there is a
  221|       |          // latency of, say, 4 cycles on getting 'consumed', then
  222|       |          // the inner loop might have a total latency of about 6 cycles.
  223|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  224|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  225|       |          // for this section of the code. Hence, there is a limit
  226|       |          // to how much we can further increase this latency before
  227|       |          // it seriously harms performance.
  228|   842k|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  229|   842k|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  230|   842k|          pos += consumed;
  231|   842k|          utf8_end_of_code_point_mask >>= consumed;
  232|   842k|        }
  233|       |        // At this point there may remain between 0 and 12 bytes in the
  234|       |        // 64-byte block. These bytes will be processed again. So we have an
  235|       |        // 80% efficiency (in the worst case). In practice we expect an
  236|       |        // 85% to 90% efficiency.
  237|  95.8k|      }
  238|   667k|    }
  239|    626|    if (errors()) {
  ------------------
  |  Branch (239:9): [True: 286, False: 340]
  ------------------
  240|    286|      return 0;
  241|    286|    }
  242|    340|    const size_t ascii =
  243|    340|        convert_ascii_tail<endian>(in, pos, size, utf16_output);
  244|    340|    pos += ascii;
  245|    340|    utf16_output += ascii;
  246|    340|    if (pos == size) {
  ------------------
  |  Branch (246:9): [True: 74, False: 266]
  ------------------
  247|     74|      return utf16_output - start;
  248|     74|    }
  249|    266|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|    266|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
  250|    266|    size_t howmany = scalar::utf8_to_utf16::convert<endian>(
  251|    266|        in + pos, size - pos, utf16_output);
  252|    266|    if (howmany == 0) {
  ------------------
  |  Branch (252:9): [True: 214, False: 52]
  ------------------
  253|    214|      return 0;
  254|    214|    }
  255|     52|    return utf16_output + howmany - start;
  256|    266|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoder16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  119|  1.12M|                                              const simd8<uint8_t> prev_input) {
  120|       |    // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+
  121|       |    // lead bytes (2, 3, 4-byte leads become large positive numbers instead of
  122|       |    // small negative numbers)
  123|  1.12M|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  124|  1.12M|    simd8<uint8_t> sc = check_special_cases(input, prev1);
  125|  1.12M|    this->error |= check_multibyte_lengths(input, prev_input, sc);
  126|  1.12M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1619check_special_casesENS1_4simd5simd8IhEES5_:
    8|  1.12M|check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
    9|       |  // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
   10|       |  // Bit 1 = Too Long (ASCII followed by continuation)
   11|       |  // Bit 2 = Overlong 3-byte
   12|       |  // Bit 4 = Surrogate
   13|       |  // Bit 5 = Overlong 2-byte
   14|       |  // Bit 7 = Two Continuations
   15|  1.12M|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   16|       |                                               // 11______ 11______
   17|  1.12M|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   18|  1.12M|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   19|  1.12M|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   20|  1.12M|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   21|  1.12M|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   22|  1.12M|  constexpr const uint8_t TOO_LARGE = 1 << 3;  // 11110100 1001____
   23|       |                                               // 11110100 101_____
   24|       |                                               // 11110101 1001____
   25|       |                                               // 11110101 101_____
   26|       |                                               // 1111011_ 1001____
   27|       |                                               // 1111011_ 101_____
   28|       |                                               // 11111___ 1001____
   29|       |                                               // 11111___ 101_____
   30|  1.12M|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   31|       |  // 11110101 1000____
   32|       |  // 1111011_ 1000____
   33|       |  // 11111___ 1000____
   34|  1.12M|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   35|       |
   36|  1.12M|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   37|       |      // 0_______ ________ <ASCII in byte 1>
   38|  1.12M|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   39|  1.12M|      TOO_LONG,
   40|       |      // 10______ ________ <continuation in byte 1>
   41|  1.12M|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   42|       |      // 1100____ ________ <two byte lead in byte 1>
   43|  1.12M|      TOO_SHORT | OVERLONG_2,
   44|       |      // 1101____ ________ <two byte lead in byte 1>
   45|  1.12M|      TOO_SHORT,
   46|       |      // 1110____ ________ <three byte lead in byte 1>
   47|  1.12M|      TOO_SHORT | OVERLONG_3 | SURROGATE,
   48|       |      // 1111____ ________ <four+ byte lead in byte 1>
   49|  1.12M|      TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4);
   50|  1.12M|  constexpr const uint8_t CARRY =
   51|  1.12M|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   52|  1.12M|  const simd8<uint8_t> byte_1_low =
   53|  1.12M|      (prev1 & 0x0F)
   54|  1.12M|          .lookup_16<uint8_t>(
   55|       |              // ____0000 ________
   56|  1.12M|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   57|       |              // ____0001 ________
   58|  1.12M|              CARRY | OVERLONG_2,
   59|       |              // ____001_ ________
   60|  1.12M|              CARRY, CARRY,
   61|       |
   62|       |              // ____0100 ________
   63|  1.12M|              CARRY | TOO_LARGE,
   64|       |              // ____0101 ________
   65|  1.12M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   66|       |              // ____011_ ________
   67|  1.12M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   68|  1.12M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   69|       |
   70|       |              // ____1___ ________
   71|  1.12M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   72|  1.12M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   73|  1.12M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   74|  1.12M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   75|  1.12M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   76|       |              // ____1101 ________
   77|  1.12M|              CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
   78|  1.12M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   79|  1.12M|              CARRY | TOO_LARGE | TOO_LARGE_1000);
   80|  1.12M|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   81|       |      // ________ 0_______ <ASCII in byte 2>
   82|  1.12M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   83|  1.12M|      TOO_SHORT, TOO_SHORT,
   84|       |
   85|       |      // ________ 1000____
   86|  1.12M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   87|  1.12M|          OVERLONG_4,
   88|       |      // ________ 1001____
   89|  1.12M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   90|       |      // ________ 101_____
   91|  1.12M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   92|  1.12M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   93|       |
   94|       |      // ________ 11______
   95|  1.12M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   96|  1.12M|  return (byte_1_high & byte_1_low & byte_2_high);
   97|  1.12M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1623check_multibyte_lengthsENS1_4simd5simd8IhEES5_S5_:
  101|  1.12M|                        const simd8<uint8_t> sc) {
  102|  1.12M|  simd8<uint8_t> prev2 = input.prev<2>(prev_input);
  103|  1.12M|  simd8<uint8_t> prev3 = input.prev<3>(prev_input);
  104|  1.12M|  simd8<uint8_t> must23 =
  105|  1.12M|      simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
  106|  1.12M|  simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
  107|  1.12M|  return must23_80 ^ sc;
  108|  1.12M|}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoder6errorsEv:
  366|   198k|  simdutf_really_inline bool errors() const {
  367|   198k|    return this->error.any_bits_set_anywhere();
  368|   198k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoder18convert_ascii_tailILNS_10endiannessE0EEEmPKcmmPDs:
  131|    702|                                                  char16_t *output) {
  132|       |    // A cheap test on the first bytes avoids any SIMD work for most non-ASCII
  133|       |    // tails.
  134|    702|    if (size - pos >= 8) {
  ------------------
  |  Branch (134:9): [True: 702, False: 0]
  ------------------
  135|    702|      uint64_t first_bytes;
  136|    702|      std::memcpy(&first_bytes, in + pos, 8);
  137|    702|      if (first_bytes & 0x8080808080808080) {
  ------------------
  |  Branch (137:11): [True: 136, False: 566]
  ------------------
  138|    136|        return 0;
  139|    136|      }
  140|    702|    }
  141|    566|    const size_t start = pos;
  142|    660|    while (pos + 64 <= size) {
  ------------------
  |  Branch (142:12): [True: 274, False: 386]
  ------------------
  143|    274|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  144|    274|      if (!input.is_ascii()) {
  ------------------
  |  Branch (144:11): [True: 180, False: 94]
  ------------------
  145|    180|        return pos - start;
  146|    180|      }
  147|     94|      input.store_ascii_as_utf16<endian>(output + (pos - start));
  148|     94|      pos += 64;
  149|     94|    }
  150|    386|    if (pos == size || size < 64) {
  ------------------
  |  Branch (150:9): [True: 24, False: 362]
  |  Branch (150:24): [True: 0, False: 362]
  ------------------
  151|     24|      return pos - start;
  152|     24|    }
  153|    362|    simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + size - 64));
  154|    362|    if (!input.is_ascii()) {
  ------------------
  |  Branch (154:9): [True: 272, False: 90]
  ------------------
  155|    272|      return pos - start;
  156|    272|    }
  157|     90|    input.store_ascii_as_utf16<endian>(output + (pos - start) -
  158|     90|                                       (pos - (size - 64)));
  159|     90|    return size - start;
  160|    362|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoder7convertILNS_10endiannessE1EEEmPKcmPDs:
  164|  1.00k|                                       char16_t *utf16_output) {
  165|  1.00k|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (165:9): [True: 306, False: 702]
  ------------------
  166|    306|      return scalar::utf8_to_utf16::convert<endian>(in, size, utf16_output);
  167|    306|    }
  168|    702|    size_t pos = 0;
  169|    702|    char16_t *start{utf16_output};
  170|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  171|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  172|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  173|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  174|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  175|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  176|    702|    size_t leading_byte = 0;
  177|    702|    size_t margin = size;
  178|  8.57k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (178:12): [True: 8.56k, False: 8]
  |  Branch (178:26): [True: 7.87k, False: 694]
  ------------------
  179|  7.87k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  180|  7.87k|    }
  181|       |    // If the input is long enough, then we have that margin-1 is the eight last
  182|       |    // leading byte.
  183|    702|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  184|   841k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (184:12): [True: 840k, False: 666]
  ------------------
  185|   840k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  186|   840k|      if (input.is_ascii()) {
  ------------------
  |  Branch (186:11): [True: 570k, False: 269k]
  ------------------
  187|   570k|        input.store_ascii_as_utf16<endian>(utf16_output);
  188|   570k|        utf16_output += 64;
  189|   570k|        pos += 64;
  190|   570k|      } else {
  191|       |        // you might think that a for-loop would work, but under Visual Studio,
  192|       |        // it is not good enough.
  193|   269k|        static_assert(
  194|   269k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  195|   269k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  196|   269k|            "We support either two or four chunks per 64-byte block.");
  197|   269k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  198|   269k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  199|   269k|          this->check_utf8_bytes(input.chunks[0], zero);
  200|   269k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  201|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  202|       |          this->check_utf8_bytes(input.chunks[0], zero);
  203|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  204|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  205|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  206|       |        }
  207|   269k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  208|   269k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (208:13): [True: 36, False: 269k]
  ------------------
  209|     36|          return 0; // error
  210|     36|        }
  211|   269k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  212|   269k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  213|       |        // We process in blocks of up to 12 bytes except possibly
  214|       |        // for fast paths which may process up to 16 bytes. For the
  215|       |        // slow path to work, we should have at least 12 input bytes left.
  216|   269k|        size_t max_starting_point = (pos + 64) - 12;
  217|       |        // Next loop is going to run at least five times.
  218|  2.64M|        while (pos < max_starting_point) {
  ------------------
  |  Branch (218:16): [True: 2.37M, False: 269k]
  ------------------
  219|       |          // Performance note: our ability to compute 'consumed' and
  220|       |          // then shift and recompute is critical. If there is a
  221|       |          // latency of, say, 4 cycles on getting 'consumed', then
  222|       |          // the inner loop might have a total latency of about 6 cycles.
  223|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  224|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  225|       |          // for this section of the code. Hence, there is a limit
  226|       |          // to how much we can further increase this latency before
  227|       |          // it seriously harms performance.
  228|  2.37M|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  229|  2.37M|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  230|  2.37M|          pos += consumed;
  231|  2.37M|          utf8_end_of_code_point_mask >>= consumed;
  232|  2.37M|        }
  233|       |        // At this point there may remain between 0 and 12 bytes in the
  234|       |        // 64-byte block. These bytes will be processed again. So we have an
  235|       |        // 80% efficiency (in the worst case). In practice we expect an
  236|       |        // 85% to 90% efficiency.
  237|   269k|      }
  238|   840k|    }
  239|    666|    if (errors()) {
  ------------------
  |  Branch (239:9): [True: 360, False: 306]
  ------------------
  240|    360|      return 0;
  241|    360|    }
  242|    306|    const size_t ascii =
  243|    306|        convert_ascii_tail<endian>(in, pos, size, utf16_output);
  244|    306|    pos += ascii;
  245|    306|    utf16_output += ascii;
  246|    306|    if (pos == size) {
  ------------------
  |  Branch (246:9): [True: 42, False: 264]
  ------------------
  247|     42|      return utf16_output - start;
  248|     42|    }
  249|    264|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|    264|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
  250|    264|    size_t howmany = scalar::utf8_to_utf16::convert<endian>(
  251|    264|        in + pos, size - pos, utf16_output);
  252|    264|    if (howmany == 0) {
  ------------------
  |  Branch (252:9): [True: 230, False: 34]
  ------------------
  253|    230|      return 0;
  254|    230|    }
  255|     34|    return utf16_output + howmany - start;
  256|    264|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoder18convert_ascii_tailILNS_10endiannessE1EEEmPKcmmPDs:
  131|    814|                                                  char16_t *output) {
  132|       |    // A cheap test on the first bytes avoids any SIMD work for most non-ASCII
  133|       |    // tails.
  134|    814|    if (size - pos >= 8) {
  ------------------
  |  Branch (134:9): [True: 814, False: 0]
  ------------------
  135|    814|      uint64_t first_bytes;
  136|    814|      std::memcpy(&first_bytes, in + pos, 8);
  137|    814|      if (first_bytes & 0x8080808080808080) {
  ------------------
  |  Branch (137:11): [True: 212, False: 602]
  ------------------
  138|    212|        return 0;
  139|    212|      }
  140|    814|    }
  141|    602|    const size_t start = pos;
  142|    678|    while (pos + 64 <= size) {
  ------------------
  |  Branch (142:12): [True: 254, False: 424]
  ------------------
  143|    254|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  144|    254|      if (!input.is_ascii()) {
  ------------------
  |  Branch (144:11): [True: 178, False: 76]
  ------------------
  145|    178|        return pos - start;
  146|    178|      }
  147|     76|      input.store_ascii_as_utf16<endian>(output + (pos - start));
  148|     76|      pos += 64;
  149|     76|    }
  150|    424|    if (pos == size || size < 64) {
  ------------------
  |  Branch (150:9): [True: 30, False: 394]
  |  Branch (150:24): [True: 0, False: 394]
  ------------------
  151|     30|      return pos - start;
  152|     30|    }
  153|    394|    simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + size - 64));
  154|    394|    if (!input.is_ascii()) {
  ------------------
  |  Branch (154:9): [True: 302, False: 92]
  ------------------
  155|    302|      return pos - start;
  156|    302|    }
  157|     92|    input.store_ascii_as_utf16<endian>(output + (pos - start) -
  158|     92|                                       (pos - (size - 64)));
  159|     92|    return size - start;
  160|    394|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoder19convert_with_errorsILNS_10endiannessE0EEENS_6resultEPKcmPDs:
  260|    746|                                                   char16_t *utf16_output) {
  261|    746|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (261:9): [True: 274, False: 472]
  ------------------
  262|    274|      return scalar::utf8_to_utf16::convert_with_errors<endian>(in, size,
  263|    274|                                                                utf16_output);
  264|    274|    }
  265|    472|    size_t pos = 0;
  266|    472|    char16_t *start{utf16_output};
  267|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  268|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  269|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  270|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  271|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  272|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  273|    472|    size_t leading_byte = 0;
  274|    472|    size_t margin = size;
  275|  7.02k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (275:12): [True: 7.01k, False: 16]
  |  Branch (275:26): [True: 6.55k, False: 456]
  ------------------
  276|  6.55k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  277|  6.55k|    }
  278|       |    // If the input is long enough, then we have that margin-1 is the eight last
  279|       |    // leading byte.
  280|    472|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  281|   340k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (281:12): [True: 339k, False: 362]
  ------------------
  282|   339k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  283|   339k|      if (input.is_ascii()) {
  ------------------
  |  Branch (283:11): [True: 266k, False: 73.6k]
  ------------------
  284|   266k|        input.store_ascii_as_utf16<endian>(utf16_output);
  285|   266k|        utf16_output += 64;
  286|   266k|        pos += 64;
  287|   266k|      } else {
  288|       |        // you might think that a for-loop would work, but under Visual Studio,
  289|       |        // it is not good enough.
  290|  73.6k|        static_assert(
  291|  73.6k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  292|  73.6k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  293|  73.6k|            "We support either two or four chunks per 64-byte block.");
  294|  73.6k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  295|  73.6k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  296|  73.6k|          this->check_utf8_bytes(input.chunks[0], zero);
  297|  73.6k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  298|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  299|       |          this->check_utf8_bytes(input.chunks[0], zero);
  300|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  301|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  302|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  303|       |        }
  304|  73.6k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  305|  73.6k|        if (errors() || (utf8_continuation_mask & 1)) {
  ------------------
  |  Branch (305:13): [True: 110, False: 73.5k]
  |  Branch (305:25): [True: 0, False: 73.5k]
  ------------------
  306|       |          // rewind_and_convert_with_errors will seek a potential error from
  307|       |          // in+pos onward, with the ability to go back up to pos bytes, and
  308|       |          // read size-pos bytes forward.
  309|    110|          SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|    110|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
  310|    110|          result res =
  311|    110|              scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  312|    110|                  pos, in + pos, size - pos, utf16_output);
  313|    110|          res.count += pos;
  314|    110|          return res;
  315|    110|        }
  316|  73.5k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  317|  73.5k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  318|       |        // We process in blocks of up to 12 bytes except possibly
  319|       |        // for fast paths which may process up to 16 bytes. For the
  320|       |        // slow path to work, we should have at least 12 input bytes left.
  321|  73.5k|        size_t max_starting_point = (pos + 64) - 12;
  322|       |        // Next loop is going to run at least five times.
  323|   718k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (323:16): [True: 644k, False: 73.5k]
  ------------------
  324|       |          // Performance note: our ability to compute 'consumed' and
  325|       |          // then shift and recompute is critical. If there is a
  326|       |          // latency of, say, 4 cycles on getting 'consumed', then
  327|       |          // the inner loop might have a total latency of about 6 cycles.
  328|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  329|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  330|       |          // for this section of the code. Hence, there is a limit
  331|       |          // to how much we can further increase this latency before
  332|       |          // it seriously harms performance.
  333|   644k|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  334|   644k|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  335|   644k|          pos += consumed;
  336|   644k|          utf8_end_of_code_point_mask >>= consumed;
  337|   644k|        }
  338|       |        // At this point there may remain between 0 and 12 bytes in the
  339|       |        // 64-byte block. These bytes will be processed again. So we have an
  340|       |        // 80% efficiency (in the worst case). In practice we expect an
  341|       |        // 85% to 90% efficiency.
  342|  73.5k|      }
  343|   339k|    }
  344|    362|    const size_t ascii =
  345|    362|        convert_ascii_tail<endian>(in, pos, size, utf16_output);
  346|    362|    pos += ascii;
  347|    362|    utf16_output += ascii;
  348|    362|    if (pos == size) {
  ------------------
  |  Branch (348:9): [True: 40, False: 322]
  ------------------
  349|     40|      return result(error_code::SUCCESS, utf16_output - start);
  350|     40|    }
  351|       |    // rewind_and_convert_with_errors will seek a potential error from in+pos
  352|       |    // onward, with the ability to go back up to pos bytes, and read size-pos
  353|       |    // bytes forward.
  354|    322|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|    322|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
  355|    322|    result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  356|    322|        pos, in + pos, size - pos, utf16_output);
  357|    322|    if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (357:9): [True: 258, False: 64]
  ------------------
  358|    258|      res.count += pos;
  359|    258|      return res;
  360|    258|    }
  361|       |    // In case of success, we want the number of words written
  362|     64|    utf16_output += res.count;
  363|     64|    return result(error_code::SUCCESS, utf16_output - start);
  364|    322|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoder19convert_with_errorsILNS_10endiannessE1EEENS_6resultEPKcmPDs:
  260|    934|                                                   char16_t *utf16_output) {
  261|    934|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (261:9): [True: 312, False: 622]
  ------------------
  262|    312|      return scalar::utf8_to_utf16::convert_with_errors<endian>(in, size,
  263|    312|                                                                utf16_output);
  264|    312|    }
  265|    622|    size_t pos = 0;
  266|    622|    char16_t *start{utf16_output};
  267|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  268|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  269|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  270|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  271|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  272|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  273|    622|    size_t leading_byte = 0;
  274|    622|    size_t margin = size;
  275|  8.27k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (275:12): [True: 8.26k, False: 12]
  |  Branch (275:26): [True: 7.65k, False: 610]
  ------------------
  276|  7.65k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  277|  7.65k|    }
  278|       |    // If the input is long enough, then we have that margin-1 is the eight last
  279|       |    // leading byte.
  280|    622|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  281|   489k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (281:12): [True: 488k, False: 508]
  ------------------
  282|   488k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  283|   488k|      if (input.is_ascii()) {
  ------------------
  |  Branch (283:11): [True: 364k, False: 123k]
  ------------------
  284|   364k|        input.store_ascii_as_utf16<endian>(utf16_output);
  285|   364k|        utf16_output += 64;
  286|   364k|        pos += 64;
  287|   364k|      } else {
  288|       |        // you might think that a for-loop would work, but under Visual Studio,
  289|       |        // it is not good enough.
  290|   123k|        static_assert(
  291|   123k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  292|   123k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  293|   123k|            "We support either two or four chunks per 64-byte block.");
  294|   123k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  295|   123k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  296|   123k|          this->check_utf8_bytes(input.chunks[0], zero);
  297|   123k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  298|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  299|       |          this->check_utf8_bytes(input.chunks[0], zero);
  300|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  301|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  302|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  303|       |        }
  304|   123k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  305|   123k|        if (errors() || (utf8_continuation_mask & 1)) {
  ------------------
  |  Branch (305:13): [True: 114, False: 123k]
  |  Branch (305:25): [True: 0, False: 123k]
  ------------------
  306|       |          // rewind_and_convert_with_errors will seek a potential error from
  307|       |          // in+pos onward, with the ability to go back up to pos bytes, and
  308|       |          // read size-pos bytes forward.
  309|    114|          SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|    114|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
  310|    114|          result res =
  311|    114|              scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  312|    114|                  pos, in + pos, size - pos, utf16_output);
  313|    114|          res.count += pos;
  314|    114|          return res;
  315|    114|        }
  316|   123k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  317|   123k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  318|       |        // We process in blocks of up to 12 bytes except possibly
  319|       |        // for fast paths which may process up to 16 bytes. For the
  320|       |        // slow path to work, we should have at least 12 input bytes left.
  321|   123k|        size_t max_starting_point = (pos + 64) - 12;
  322|       |        // Next loop is going to run at least five times.
  323|  1.21M|        while (pos < max_starting_point) {
  ------------------
  |  Branch (323:16): [True: 1.08M, False: 123k]
  ------------------
  324|       |          // Performance note: our ability to compute 'consumed' and
  325|       |          // then shift and recompute is critical. If there is a
  326|       |          // latency of, say, 4 cycles on getting 'consumed', then
  327|       |          // the inner loop might have a total latency of about 6 cycles.
  328|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  329|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  330|       |          // for this section of the code. Hence, there is a limit
  331|       |          // to how much we can further increase this latency before
  332|       |          // it seriously harms performance.
  333|  1.08M|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  334|  1.08M|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  335|  1.08M|          pos += consumed;
  336|  1.08M|          utf8_end_of_code_point_mask >>= consumed;
  337|  1.08M|        }
  338|       |        // At this point there may remain between 0 and 12 bytes in the
  339|       |        // 64-byte block. These bytes will be processed again. So we have an
  340|       |        // 80% efficiency (in the worst case). In practice we expect an
  341|       |        // 85% to 90% efficiency.
  342|   123k|      }
  343|   488k|    }
  344|    508|    const size_t ascii =
  345|    508|        convert_ascii_tail<endian>(in, pos, size, utf16_output);
  346|    508|    pos += ascii;
  347|    508|    utf16_output += ascii;
  348|    508|    if (pos == size) {
  ------------------
  |  Branch (348:9): [True: 80, False: 428]
  ------------------
  349|     80|      return result(error_code::SUCCESS, utf16_output - start);
  350|     80|    }
  351|       |    // rewind_and_convert_with_errors will seek a potential error from in+pos
  352|       |    // onward, with the ability to go back up to pos bytes, and read size-pos
  353|       |    // bytes forward.
  354|    428|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|    428|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
  355|    428|    result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  356|    428|        pos, in + pos, size - pos, utf16_output);
  357|    428|    if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (357:9): [True: 294, False: 134]
  ------------------
  358|    294|      res.count += pos;
  359|    294|      return res;
  360|    294|    }
  361|       |    // In case of success, we want the number of words written
  362|    134|    utf16_output += res.count;
  363|    134|    return result(error_code::SUCCESS, utf16_output - start);
  364|    428|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoder7convertILNS_10endiannessE0EEEmPKcmPDs:
  164|    950|                                       char16_t *utf16_output) {
  165|    950|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (165:9): [True: 294, False: 656]
  ------------------
  166|    294|      return scalar::utf8_to_utf16::convert<endian>(in, size, utf16_output);
  167|    294|    }
  168|    656|    size_t pos = 0;
  169|    656|    char16_t *start{utf16_output};
  170|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  171|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  172|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  173|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  174|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  175|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  176|    656|    size_t leading_byte = 0;
  177|    656|    size_t margin = size;
  178|  9.10k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (178:12): [True: 9.08k, False: 16]
  |  Branch (178:26): [True: 8.44k, False: 640]
  ------------------
  179|  8.44k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  180|  8.44k|    }
  181|       |    // If the input is long enough, then we have that margin-1 is the eight last
  182|       |    // leading byte.
  183|    656|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  184|   668k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (184:12): [True: 667k, False: 626]
  ------------------
  185|   667k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  186|   667k|      if (input.is_ascii()) {
  ------------------
  |  Branch (186:11): [True: 571k, False: 95.8k]
  ------------------
  187|   571k|        input.store_ascii_as_utf16<endian>(utf16_output);
  188|   571k|        utf16_output += 64;
  189|   571k|        pos += 64;
  190|   571k|      } else {
  191|       |        // you might think that a for-loop would work, but under Visual Studio,
  192|       |        // it is not good enough.
  193|  95.8k|        static_assert(
  194|  95.8k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  195|  95.8k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  196|  95.8k|            "We support either two or four chunks per 64-byte block.");
  197|  95.8k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  198|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  199|       |          this->check_utf8_bytes(input.chunks[0], zero);
  200|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  201|  95.8k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  202|  95.8k|          this->check_utf8_bytes(input.chunks[0], zero);
  203|  95.8k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  204|  95.8k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  205|  95.8k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  206|  95.8k|        }
  207|  95.8k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  208|  95.8k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (208:13): [True: 30, False: 95.8k]
  ------------------
  209|     30|          return 0; // error
  210|     30|        }
  211|  95.8k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  212|  95.8k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  213|       |        // We process in blocks of up to 12 bytes except possibly
  214|       |        // for fast paths which may process up to 16 bytes. For the
  215|       |        // slow path to work, we should have at least 12 input bytes left.
  216|  95.8k|        size_t max_starting_point = (pos + 64) - 12;
  217|       |        // Next loop is going to run at least five times.
  218|   938k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (218:16): [True: 842k, False: 95.8k]
  ------------------
  219|       |          // Performance note: our ability to compute 'consumed' and
  220|       |          // then shift and recompute is critical. If there is a
  221|       |          // latency of, say, 4 cycles on getting 'consumed', then
  222|       |          // the inner loop might have a total latency of about 6 cycles.
  223|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  224|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  225|       |          // for this section of the code. Hence, there is a limit
  226|       |          // to how much we can further increase this latency before
  227|       |          // it seriously harms performance.
  228|   842k|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  229|   842k|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  230|   842k|          pos += consumed;
  231|   842k|          utf8_end_of_code_point_mask >>= consumed;
  232|   842k|        }
  233|       |        // At this point there may remain between 0 and 12 bytes in the
  234|       |        // 64-byte block. These bytes will be processed again. So we have an
  235|       |        // 80% efficiency (in the worst case). In practice we expect an
  236|       |        // 85% to 90% efficiency.
  237|  95.8k|      }
  238|   667k|    }
  239|    626|    if (errors()) {
  ------------------
  |  Branch (239:9): [True: 286, False: 340]
  ------------------
  240|    286|      return 0;
  241|    286|    }
  242|    340|    const size_t ascii =
  243|    340|        convert_ascii_tail<endian>(in, pos, size, utf16_output);
  244|    340|    pos += ascii;
  245|    340|    utf16_output += ascii;
  246|    340|    if (pos == size) {
  ------------------
  |  Branch (246:9): [True: 74, False: 266]
  ------------------
  247|     74|      return utf16_output - start;
  248|     74|    }
  249|    266|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   16|    266|#define SIMDUTF_VZEROUPPER() (void)0
  ------------------
  250|    266|    size_t howmany = scalar::utf8_to_utf16::convert<endian>(
  251|    266|        in + pos, size - pos, utf16_output);
  252|    266|    if (howmany == 0) {
  ------------------
  |  Branch (252:9): [True: 214, False: 52]
  ------------------
  253|    214|      return 0;
  254|    214|    }
  255|     52|    return utf16_output + howmany - start;
  256|    266|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoder16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  119|  2.25M|                                              const simd8<uint8_t> prev_input) {
  120|       |    // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+
  121|       |    // lead bytes (2, 3, 4-byte leads become large positive numbers instead of
  122|       |    // small negative numbers)
  123|  2.25M|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  124|  2.25M|    simd8<uint8_t> sc = check_special_cases(input, prev1);
  125|  2.25M|    this->error |= check_multibyte_lengths(input, prev_input, sc);
  126|  2.25M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1619check_special_casesENS1_4simd5simd8IhEES5_:
    8|  2.25M|check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
    9|       |  // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
   10|       |  // Bit 1 = Too Long (ASCII followed by continuation)
   11|       |  // Bit 2 = Overlong 3-byte
   12|       |  // Bit 4 = Surrogate
   13|       |  // Bit 5 = Overlong 2-byte
   14|       |  // Bit 7 = Two Continuations
   15|  2.25M|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   16|       |                                               // 11______ 11______
   17|  2.25M|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   18|  2.25M|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   19|  2.25M|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   20|  2.25M|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   21|  2.25M|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   22|  2.25M|  constexpr const uint8_t TOO_LARGE = 1 << 3;  // 11110100 1001____
   23|       |                                               // 11110100 101_____
   24|       |                                               // 11110101 1001____
   25|       |                                               // 11110101 101_____
   26|       |                                               // 1111011_ 1001____
   27|       |                                               // 1111011_ 101_____
   28|       |                                               // 11111___ 1001____
   29|       |                                               // 11111___ 101_____
   30|  2.25M|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   31|       |  // 11110101 1000____
   32|       |  // 1111011_ 1000____
   33|       |  // 11111___ 1000____
   34|  2.25M|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   35|       |
   36|  2.25M|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   37|       |      // 0_______ ________ <ASCII in byte 1>
   38|  2.25M|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   39|  2.25M|      TOO_LONG,
   40|       |      // 10______ ________ <continuation in byte 1>
   41|  2.25M|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   42|       |      // 1100____ ________ <two byte lead in byte 1>
   43|  2.25M|      TOO_SHORT | OVERLONG_2,
   44|       |      // 1101____ ________ <two byte lead in byte 1>
   45|  2.25M|      TOO_SHORT,
   46|       |      // 1110____ ________ <three byte lead in byte 1>
   47|  2.25M|      TOO_SHORT | OVERLONG_3 | SURROGATE,
   48|       |      // 1111____ ________ <four+ byte lead in byte 1>
   49|  2.25M|      TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4);
   50|  2.25M|  constexpr const uint8_t CARRY =
   51|  2.25M|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   52|  2.25M|  const simd8<uint8_t> byte_1_low =
   53|  2.25M|      (prev1 & 0x0F)
   54|  2.25M|          .lookup_16<uint8_t>(
   55|       |              // ____0000 ________
   56|  2.25M|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   57|       |              // ____0001 ________
   58|  2.25M|              CARRY | OVERLONG_2,
   59|       |              // ____001_ ________
   60|  2.25M|              CARRY, CARRY,
   61|       |
   62|       |              // ____0100 ________
   63|  2.25M|              CARRY | TOO_LARGE,
   64|       |              // ____0101 ________
   65|  2.25M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   66|       |              // ____011_ ________
   67|  2.25M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   68|  2.25M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   69|       |
   70|       |              // ____1___ ________
   71|  2.25M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   72|  2.25M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   73|  2.25M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   74|  2.25M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   75|  2.25M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   76|       |              // ____1101 ________
   77|  2.25M|              CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
   78|  2.25M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   79|  2.25M|              CARRY | TOO_LARGE | TOO_LARGE_1000);
   80|  2.25M|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   81|       |      // ________ 0_______ <ASCII in byte 2>
   82|  2.25M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   83|  2.25M|      TOO_SHORT, TOO_SHORT,
   84|       |
   85|       |      // ________ 1000____
   86|  2.25M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   87|  2.25M|          OVERLONG_4,
   88|       |      // ________ 1001____
   89|  2.25M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   90|       |      // ________ 101_____
   91|  2.25M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   92|  2.25M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   93|       |
   94|       |      // ________ 11______
   95|  2.25M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   96|  2.25M|  return (byte_1_high & byte_1_low & byte_2_high);
   97|  2.25M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1623check_multibyte_lengthsENS1_4simd5simd8IhEES5_S5_:
  101|  2.25M|                        const simd8<uint8_t> sc) {
  102|  2.25M|  simd8<uint8_t> prev2 = input.prev<2>(prev_input);
  103|  2.25M|  simd8<uint8_t> prev3 = input.prev<3>(prev_input);
  104|  2.25M|  simd8<uint8_t> must23 =
  105|  2.25M|      simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
  106|  2.25M|  simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
  107|  2.25M|  return must23_80 ^ sc;
  108|  2.25M|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoder6errorsEv:
  366|   198k|  simdutf_really_inline bool errors() const {
  367|   198k|    return this->error.any_bits_set_anywhere();
  368|   198k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoder18convert_ascii_tailILNS_10endiannessE0EEEmPKcmmPDs:
  131|    702|                                                  char16_t *output) {
  132|       |    // A cheap test on the first bytes avoids any SIMD work for most non-ASCII
  133|       |    // tails.
  134|    702|    if (size - pos >= 8) {
  ------------------
  |  Branch (134:9): [True: 702, False: 0]
  ------------------
  135|    702|      uint64_t first_bytes;
  136|    702|      std::memcpy(&first_bytes, in + pos, 8);
  137|    702|      if (first_bytes & 0x8080808080808080) {
  ------------------
  |  Branch (137:11): [True: 136, False: 566]
  ------------------
  138|    136|        return 0;
  139|    136|      }
  140|    702|    }
  141|    566|    const size_t start = pos;
  142|    660|    while (pos + 64 <= size) {
  ------------------
  |  Branch (142:12): [True: 274, False: 386]
  ------------------
  143|    274|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  144|    274|      if (!input.is_ascii()) {
  ------------------
  |  Branch (144:11): [True: 180, False: 94]
  ------------------
  145|    180|        return pos - start;
  146|    180|      }
  147|     94|      input.store_ascii_as_utf16<endian>(output + (pos - start));
  148|     94|      pos += 64;
  149|     94|    }
  150|    386|    if (pos == size || size < 64) {
  ------------------
  |  Branch (150:9): [True: 24, False: 362]
  |  Branch (150:24): [True: 0, False: 362]
  ------------------
  151|     24|      return pos - start;
  152|     24|    }
  153|    362|    simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + size - 64));
  154|    362|    if (!input.is_ascii()) {
  ------------------
  |  Branch (154:9): [True: 272, False: 90]
  ------------------
  155|    272|      return pos - start;
  156|    272|    }
  157|     90|    input.store_ascii_as_utf16<endian>(output + (pos - start) -
  158|     90|                                       (pos - (size - 64)));
  159|     90|    return size - start;
  160|    362|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoder7convertILNS_10endiannessE1EEEmPKcmPDs:
  164|  1.00k|                                       char16_t *utf16_output) {
  165|  1.00k|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (165:9): [True: 306, False: 702]
  ------------------
  166|    306|      return scalar::utf8_to_utf16::convert<endian>(in, size, utf16_output);
  167|    306|    }
  168|    702|    size_t pos = 0;
  169|    702|    char16_t *start{utf16_output};
  170|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  171|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  172|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  173|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  174|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  175|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  176|    702|    size_t leading_byte = 0;
  177|    702|    size_t margin = size;
  178|  8.57k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (178:12): [True: 8.56k, False: 8]
  |  Branch (178:26): [True: 7.87k, False: 694]
  ------------------
  179|  7.87k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  180|  7.87k|    }
  181|       |    // If the input is long enough, then we have that margin-1 is the eight last
  182|       |    // leading byte.
  183|    702|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  184|   841k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (184:12): [True: 840k, False: 666]
  ------------------
  185|   840k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  186|   840k|      if (input.is_ascii()) {
  ------------------
  |  Branch (186:11): [True: 570k, False: 269k]
  ------------------
  187|   570k|        input.store_ascii_as_utf16<endian>(utf16_output);
  188|   570k|        utf16_output += 64;
  189|   570k|        pos += 64;
  190|   570k|      } else {
  191|       |        // you might think that a for-loop would work, but under Visual Studio,
  192|       |        // it is not good enough.
  193|   269k|        static_assert(
  194|   269k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  195|   269k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  196|   269k|            "We support either two or four chunks per 64-byte block.");
  197|   269k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  198|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  199|       |          this->check_utf8_bytes(input.chunks[0], zero);
  200|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  201|   269k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  202|   269k|          this->check_utf8_bytes(input.chunks[0], zero);
  203|   269k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  204|   269k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  205|   269k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  206|   269k|        }
  207|   269k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  208|   269k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (208:13): [True: 36, False: 269k]
  ------------------
  209|     36|          return 0; // error
  210|     36|        }
  211|   269k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  212|   269k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  213|       |        // We process in blocks of up to 12 bytes except possibly
  214|       |        // for fast paths which may process up to 16 bytes. For the
  215|       |        // slow path to work, we should have at least 12 input bytes left.
  216|   269k|        size_t max_starting_point = (pos + 64) - 12;
  217|       |        // Next loop is going to run at least five times.
  218|  2.64M|        while (pos < max_starting_point) {
  ------------------
  |  Branch (218:16): [True: 2.37M, False: 269k]
  ------------------
  219|       |          // Performance note: our ability to compute 'consumed' and
  220|       |          // then shift and recompute is critical. If there is a
  221|       |          // latency of, say, 4 cycles on getting 'consumed', then
  222|       |          // the inner loop might have a total latency of about 6 cycles.
  223|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  224|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  225|       |          // for this section of the code. Hence, there is a limit
  226|       |          // to how much we can further increase this latency before
  227|       |          // it seriously harms performance.
  228|  2.37M|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  229|  2.37M|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  230|  2.37M|          pos += consumed;
  231|  2.37M|          utf8_end_of_code_point_mask >>= consumed;
  232|  2.37M|        }
  233|       |        // At this point there may remain between 0 and 12 bytes in the
  234|       |        // 64-byte block. These bytes will be processed again. So we have an
  235|       |        // 80% efficiency (in the worst case). In practice we expect an
  236|       |        // 85% to 90% efficiency.
  237|   269k|      }
  238|   840k|    }
  239|    666|    if (errors()) {
  ------------------
  |  Branch (239:9): [True: 360, False: 306]
  ------------------
  240|    360|      return 0;
  241|    360|    }
  242|    306|    const size_t ascii =
  243|    306|        convert_ascii_tail<endian>(in, pos, size, utf16_output);
  244|    306|    pos += ascii;
  245|    306|    utf16_output += ascii;
  246|    306|    if (pos == size) {
  ------------------
  |  Branch (246:9): [True: 42, False: 264]
  ------------------
  247|     42|      return utf16_output - start;
  248|     42|    }
  249|    264|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   16|    264|#define SIMDUTF_VZEROUPPER() (void)0
  ------------------
  250|    264|    size_t howmany = scalar::utf8_to_utf16::convert<endian>(
  251|    264|        in + pos, size - pos, utf16_output);
  252|    264|    if (howmany == 0) {
  ------------------
  |  Branch (252:9): [True: 230, False: 34]
  ------------------
  253|    230|      return 0;
  254|    230|    }
  255|     34|    return utf16_output + howmany - start;
  256|    264|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoder18convert_ascii_tailILNS_10endiannessE1EEEmPKcmmPDs:
  131|    814|                                                  char16_t *output) {
  132|       |    // A cheap test on the first bytes avoids any SIMD work for most non-ASCII
  133|       |    // tails.
  134|    814|    if (size - pos >= 8) {
  ------------------
  |  Branch (134:9): [True: 814, False: 0]
  ------------------
  135|    814|      uint64_t first_bytes;
  136|    814|      std::memcpy(&first_bytes, in + pos, 8);
  137|    814|      if (first_bytes & 0x8080808080808080) {
  ------------------
  |  Branch (137:11): [True: 212, False: 602]
  ------------------
  138|    212|        return 0;
  139|    212|      }
  140|    814|    }
  141|    602|    const size_t start = pos;
  142|    678|    while (pos + 64 <= size) {
  ------------------
  |  Branch (142:12): [True: 254, False: 424]
  ------------------
  143|    254|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  144|    254|      if (!input.is_ascii()) {
  ------------------
  |  Branch (144:11): [True: 178, False: 76]
  ------------------
  145|    178|        return pos - start;
  146|    178|      }
  147|     76|      input.store_ascii_as_utf16<endian>(output + (pos - start));
  148|     76|      pos += 64;
  149|     76|    }
  150|    424|    if (pos == size || size < 64) {
  ------------------
  |  Branch (150:9): [True: 30, False: 394]
  |  Branch (150:24): [True: 0, False: 394]
  ------------------
  151|     30|      return pos - start;
  152|     30|    }
  153|    394|    simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + size - 64));
  154|    394|    if (!input.is_ascii()) {
  ------------------
  |  Branch (154:9): [True: 302, False: 92]
  ------------------
  155|    302|      return pos - start;
  156|    302|    }
  157|     92|    input.store_ascii_as_utf16<endian>(output + (pos - start) -
  158|     92|                                       (pos - (size - 64)));
  159|     92|    return size - start;
  160|    394|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoder19convert_with_errorsILNS_10endiannessE0EEENS_6resultEPKcmPDs:
  260|    746|                                                   char16_t *utf16_output) {
  261|    746|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (261:9): [True: 274, False: 472]
  ------------------
  262|    274|      return scalar::utf8_to_utf16::convert_with_errors<endian>(in, size,
  263|    274|                                                                utf16_output);
  264|    274|    }
  265|    472|    size_t pos = 0;
  266|    472|    char16_t *start{utf16_output};
  267|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  268|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  269|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  270|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  271|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  272|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  273|    472|    size_t leading_byte = 0;
  274|    472|    size_t margin = size;
  275|  7.02k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (275:12): [True: 7.01k, False: 16]
  |  Branch (275:26): [True: 6.55k, False: 456]
  ------------------
  276|  6.55k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  277|  6.55k|    }
  278|       |    // If the input is long enough, then we have that margin-1 is the eight last
  279|       |    // leading byte.
  280|    472|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  281|   340k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (281:12): [True: 339k, False: 362]
  ------------------
  282|   339k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  283|   339k|      if (input.is_ascii()) {
  ------------------
  |  Branch (283:11): [True: 266k, False: 73.6k]
  ------------------
  284|   266k|        input.store_ascii_as_utf16<endian>(utf16_output);
  285|   266k|        utf16_output += 64;
  286|   266k|        pos += 64;
  287|   266k|      } else {
  288|       |        // you might think that a for-loop would work, but under Visual Studio,
  289|       |        // it is not good enough.
  290|  73.6k|        static_assert(
  291|  73.6k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  292|  73.6k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  293|  73.6k|            "We support either two or four chunks per 64-byte block.");
  294|  73.6k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  295|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  296|       |          this->check_utf8_bytes(input.chunks[0], zero);
  297|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  298|  73.6k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  299|  73.6k|          this->check_utf8_bytes(input.chunks[0], zero);
  300|  73.6k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  301|  73.6k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  302|  73.6k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  303|  73.6k|        }
  304|  73.6k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  305|  73.6k|        if (errors() || (utf8_continuation_mask & 1)) {
  ------------------
  |  Branch (305:13): [True: 110, False: 73.5k]
  |  Branch (305:25): [True: 0, False: 73.5k]
  ------------------
  306|       |          // rewind_and_convert_with_errors will seek a potential error from
  307|       |          // in+pos onward, with the ability to go back up to pos bytes, and
  308|       |          // read size-pos bytes forward.
  309|    110|          SIMDUTF_VZEROUPPER();
  ------------------
  |  |   16|    110|#define SIMDUTF_VZEROUPPER() (void)0
  ------------------
  310|    110|          result res =
  311|    110|              scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  312|    110|                  pos, in + pos, size - pos, utf16_output);
  313|    110|          res.count += pos;
  314|    110|          return res;
  315|    110|        }
  316|  73.5k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  317|  73.5k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  318|       |        // We process in blocks of up to 12 bytes except possibly
  319|       |        // for fast paths which may process up to 16 bytes. For the
  320|       |        // slow path to work, we should have at least 12 input bytes left.
  321|  73.5k|        size_t max_starting_point = (pos + 64) - 12;
  322|       |        // Next loop is going to run at least five times.
  323|   718k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (323:16): [True: 644k, False: 73.5k]
  ------------------
  324|       |          // Performance note: our ability to compute 'consumed' and
  325|       |          // then shift and recompute is critical. If there is a
  326|       |          // latency of, say, 4 cycles on getting 'consumed', then
  327|       |          // the inner loop might have a total latency of about 6 cycles.
  328|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  329|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  330|       |          // for this section of the code. Hence, there is a limit
  331|       |          // to how much we can further increase this latency before
  332|       |          // it seriously harms performance.
  333|   644k|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  334|   644k|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  335|   644k|          pos += consumed;
  336|   644k|          utf8_end_of_code_point_mask >>= consumed;
  337|   644k|        }
  338|       |        // At this point there may remain between 0 and 12 bytes in the
  339|       |        // 64-byte block. These bytes will be processed again. So we have an
  340|       |        // 80% efficiency (in the worst case). In practice we expect an
  341|       |        // 85% to 90% efficiency.
  342|  73.5k|      }
  343|   339k|    }
  344|    362|    const size_t ascii =
  345|    362|        convert_ascii_tail<endian>(in, pos, size, utf16_output);
  346|    362|    pos += ascii;
  347|    362|    utf16_output += ascii;
  348|    362|    if (pos == size) {
  ------------------
  |  Branch (348:9): [True: 40, False: 322]
  ------------------
  349|     40|      return result(error_code::SUCCESS, utf16_output - start);
  350|     40|    }
  351|       |    // rewind_and_convert_with_errors will seek a potential error from in+pos
  352|       |    // onward, with the ability to go back up to pos bytes, and read size-pos
  353|       |    // bytes forward.
  354|    322|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   16|    322|#define SIMDUTF_VZEROUPPER() (void)0
  ------------------
  355|    322|    result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  356|    322|        pos, in + pos, size - pos, utf16_output);
  357|    322|    if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (357:9): [True: 258, False: 64]
  ------------------
  358|    258|      res.count += pos;
  359|    258|      return res;
  360|    258|    }
  361|       |    // In case of success, we want the number of words written
  362|     64|    utf16_output += res.count;
  363|     64|    return result(error_code::SUCCESS, utf16_output - start);
  364|    322|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoder19convert_with_errorsILNS_10endiannessE1EEENS_6resultEPKcmPDs:
  260|    934|                                                   char16_t *utf16_output) {
  261|    934|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (261:9): [True: 312, False: 622]
  ------------------
  262|    312|      return scalar::utf8_to_utf16::convert_with_errors<endian>(in, size,
  263|    312|                                                                utf16_output);
  264|    312|    }
  265|    622|    size_t pos = 0;
  266|    622|    char16_t *start{utf16_output};
  267|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  268|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  269|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  270|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  271|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  272|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  273|    622|    size_t leading_byte = 0;
  274|    622|    size_t margin = size;
  275|  8.27k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (275:12): [True: 8.26k, False: 12]
  |  Branch (275:26): [True: 7.65k, False: 610]
  ------------------
  276|  7.65k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  277|  7.65k|    }
  278|       |    // If the input is long enough, then we have that margin-1 is the eight last
  279|       |    // leading byte.
  280|    622|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  281|   489k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (281:12): [True: 488k, False: 508]
  ------------------
  282|   488k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  283|   488k|      if (input.is_ascii()) {
  ------------------
  |  Branch (283:11): [True: 364k, False: 123k]
  ------------------
  284|   364k|        input.store_ascii_as_utf16<endian>(utf16_output);
  285|   364k|        utf16_output += 64;
  286|   364k|        pos += 64;
  287|   364k|      } else {
  288|       |        // you might think that a for-loop would work, but under Visual Studio,
  289|       |        // it is not good enough.
  290|   123k|        static_assert(
  291|   123k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  292|   123k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  293|   123k|            "We support either two or four chunks per 64-byte block.");
  294|   123k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  295|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  296|       |          this->check_utf8_bytes(input.chunks[0], zero);
  297|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  298|   123k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  299|   123k|          this->check_utf8_bytes(input.chunks[0], zero);
  300|   123k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  301|   123k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  302|   123k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  303|   123k|        }
  304|   123k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  305|   123k|        if (errors() || (utf8_continuation_mask & 1)) {
  ------------------
  |  Branch (305:13): [True: 114, False: 123k]
  |  Branch (305:25): [True: 0, False: 123k]
  ------------------
  306|       |          // rewind_and_convert_with_errors will seek a potential error from
  307|       |          // in+pos onward, with the ability to go back up to pos bytes, and
  308|       |          // read size-pos bytes forward.
  309|    114|          SIMDUTF_VZEROUPPER();
  ------------------
  |  |   16|    114|#define SIMDUTF_VZEROUPPER() (void)0
  ------------------
  310|    114|          result res =
  311|    114|              scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  312|    114|                  pos, in + pos, size - pos, utf16_output);
  313|    114|          res.count += pos;
  314|    114|          return res;
  315|    114|        }
  316|   123k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  317|   123k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  318|       |        // We process in blocks of up to 12 bytes except possibly
  319|       |        // for fast paths which may process up to 16 bytes. For the
  320|       |        // slow path to work, we should have at least 12 input bytes left.
  321|   123k|        size_t max_starting_point = (pos + 64) - 12;
  322|       |        // Next loop is going to run at least five times.
  323|  1.21M|        while (pos < max_starting_point) {
  ------------------
  |  Branch (323:16): [True: 1.08M, False: 123k]
  ------------------
  324|       |          // Performance note: our ability to compute 'consumed' and
  325|       |          // then shift and recompute is critical. If there is a
  326|       |          // latency of, say, 4 cycles on getting 'consumed', then
  327|       |          // the inner loop might have a total latency of about 6 cycles.
  328|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  329|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  330|       |          // for this section of the code. Hence, there is a limit
  331|       |          // to how much we can further increase this latency before
  332|       |          // it seriously harms performance.
  333|  1.08M|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  334|  1.08M|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  335|  1.08M|          pos += consumed;
  336|  1.08M|          utf8_end_of_code_point_mask >>= consumed;
  337|  1.08M|        }
  338|       |        // At this point there may remain between 0 and 12 bytes in the
  339|       |        // 64-byte block. These bytes will be processed again. So we have an
  340|       |        // 80% efficiency (in the worst case). In practice we expect an
  341|       |        // 85% to 90% efficiency.
  342|   123k|      }
  343|   488k|    }
  344|    508|    const size_t ascii =
  345|    508|        convert_ascii_tail<endian>(in, pos, size, utf16_output);
  346|    508|    pos += ascii;
  347|    508|    utf16_output += ascii;
  348|    508|    if (pos == size) {
  ------------------
  |  Branch (348:9): [True: 80, False: 428]
  ------------------
  349|     80|      return result(error_code::SUCCESS, utf16_output - start);
  350|     80|    }
  351|       |    // rewind_and_convert_with_errors will seek a potential error from in+pos
  352|       |    // onward, with the ability to go back up to pos bytes, and read size-pos
  353|       |    // bytes forward.
  354|    428|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   16|    428|#define SIMDUTF_VZEROUPPER() (void)0
  ------------------
  355|    428|    result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  356|    428|        pos, in + pos, size - pos, utf16_output);
  357|    428|    if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (357:9): [True: 294, False: 134]
  ------------------
  358|    294|      res.count += pos;
  359|    294|      return res;
  360|    294|    }
  361|       |    // In case of success, we want the number of words written
  362|    134|    utf16_output += res.count;
  363|    134|    return result(error_code::SUCCESS, utf16_output - start);
  364|    428|  }

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1613convert_validILNS_10endiannessE0EEEmPKcmPDs:
   10|    380|                                         char16_t *utf16_output) noexcept {
   11|       |  // The implementation is not specific to haswell and should be moved to the
   12|       |  // generic directory.
   13|    380|  size_t pos = 0;
   14|    380|  char16_t *start{utf16_output};
   15|    380|  const size_t safety_margin = 16; // to avoid overruns!
   16|   451k|  while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (16:10): [True: 451k, False: 380]
  ------------------
   17|       |    // this loop could be unrolled further. For example, we could process the
   18|       |    // mask far more than 64 bytes.
   19|   451k|    simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
   20|   451k|    if (in.is_ascii()) {
  ------------------
  |  Branch (20:9): [True: 304k, False: 146k]
  ------------------
   21|   304k|      in.store_ascii_as_utf16<endian>(utf16_output);
   22|   304k|      utf16_output += 64;
   23|   304k|      pos += 64;
   24|   304k|    } else {
   25|       |      // Slow path. We hope that the compiler will recognize that this is a slow
   26|       |      // path. Anything that is not a continuation mask is a 'leading byte',
   27|       |      // that is, the start of a new code point.
   28|   146k|      uint64_t utf8_continuation_mask = in.lt(-65 + 1);
   29|       |      // -65 is 0b10111111 in two-complement's, so largest possible continuation
   30|       |      // byte
   31|   146k|      uint64_t utf8_leading_mask = ~utf8_continuation_mask;
   32|       |      // The *start* of code points is not so useful, rather, we want the *end*
   33|       |      // of code points.
   34|   146k|      uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
   35|       |      // We process in blocks of up to 12 bytes except possibly
   36|       |      // for fast paths which may process up to 16 bytes. For the
   37|       |      // slow path to work, we should have at least 12 input bytes left.
   38|   146k|      size_t max_starting_point = (pos + 64) - 12;
   39|       |      // Next loop is going to run at least five times when using solely
   40|       |      // the slow/regular path, and at least four times if there are fast paths.
   41|  1.45M|      while (pos < max_starting_point) {
  ------------------
  |  Branch (41:14): [True: 1.30M, False: 146k]
  ------------------
   42|       |        // Performance note: our ability to compute 'consumed' and
   43|       |        // then shift and recompute is critical. If there is a
   44|       |        // latency of, say, 4 cycles on getting 'consumed', then
   45|       |        // the inner loop might have a total latency of about 6 cycles.
   46|       |        // Yet we process between 6 to 12 inputs bytes, thus we get
   47|       |        // a speed limit between 1 cycle/byte and 0.5 cycle/byte
   48|       |        // for this section of the code. Hence, there is a limit
   49|       |        // to how much we can further increase this latency before
   50|       |        // it seriously harms performance.
   51|       |        //
   52|       |        // Thus we may allow convert_masked_utf8_to_utf16 to process
   53|       |        // more bytes at a time under a fast-path mode where 16 bytes
   54|       |        // are consumed at once (e.g., when encountering ASCII).
   55|  1.30M|        size_t consumed = convert_masked_utf8_to_utf16<endian>(
   56|  1.30M|            input + pos, utf8_end_of_code_point_mask, utf16_output);
   57|  1.30M|        pos += consumed;
   58|  1.30M|        utf8_end_of_code_point_mask >>= consumed;
   59|  1.30M|      }
   60|       |      // At this point there may remain between 0 and 12 bytes in the
   61|       |      // 64-byte block. These bytes will be processed again. So we have an
   62|       |      // 80% efficiency (in the worst case). In practice we expect an
   63|       |      // 85% to 90% efficiency.
   64|   146k|    }
   65|   451k|  }
   66|    380|  utf16_output += scalar::utf8_to_utf16::convert_valid<endian>(
   67|    380|      input + pos, size - pos, utf16_output);
   68|    380|  return utf16_output - start;
   69|    380|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1613convert_validILNS_10endiannessE1EEEmPKcmPDs:
   10|    518|                                         char16_t *utf16_output) noexcept {
   11|       |  // The implementation is not specific to haswell and should be moved to the
   12|       |  // generic directory.
   13|    518|  size_t pos = 0;
   14|    518|  char16_t *start{utf16_output};
   15|    518|  const size_t safety_margin = 16; // to avoid overruns!
   16|   454k|  while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (16:10): [True: 453k, False: 518]
  ------------------
   17|       |    // this loop could be unrolled further. For example, we could process the
   18|       |    // mask far more than 64 bytes.
   19|   453k|    simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
   20|   453k|    if (in.is_ascii()) {
  ------------------
  |  Branch (20:9): [True: 382k, False: 71.0k]
  ------------------
   21|   382k|      in.store_ascii_as_utf16<endian>(utf16_output);
   22|   382k|      utf16_output += 64;
   23|   382k|      pos += 64;
   24|   382k|    } else {
   25|       |      // Slow path. We hope that the compiler will recognize that this is a slow
   26|       |      // path. Anything that is not a continuation mask is a 'leading byte',
   27|       |      // that is, the start of a new code point.
   28|  71.0k|      uint64_t utf8_continuation_mask = in.lt(-65 + 1);
   29|       |      // -65 is 0b10111111 in two-complement's, so largest possible continuation
   30|       |      // byte
   31|  71.0k|      uint64_t utf8_leading_mask = ~utf8_continuation_mask;
   32|       |      // The *start* of code points is not so useful, rather, we want the *end*
   33|       |      // of code points.
   34|  71.0k|      uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
   35|       |      // We process in blocks of up to 12 bytes except possibly
   36|       |      // for fast paths which may process up to 16 bytes. For the
   37|       |      // slow path to work, we should have at least 12 input bytes left.
   38|  71.0k|      size_t max_starting_point = (pos + 64) - 12;
   39|       |      // Next loop is going to run at least five times when using solely
   40|       |      // the slow/regular path, and at least four times if there are fast paths.
   41|   687k|      while (pos < max_starting_point) {
  ------------------
  |  Branch (41:14): [True: 616k, False: 71.0k]
  ------------------
   42|       |        // Performance note: our ability to compute 'consumed' and
   43|       |        // then shift and recompute is critical. If there is a
   44|       |        // latency of, say, 4 cycles on getting 'consumed', then
   45|       |        // the inner loop might have a total latency of about 6 cycles.
   46|       |        // Yet we process between 6 to 12 inputs bytes, thus we get
   47|       |        // a speed limit between 1 cycle/byte and 0.5 cycle/byte
   48|       |        // for this section of the code. Hence, there is a limit
   49|       |        // to how much we can further increase this latency before
   50|       |        // it seriously harms performance.
   51|       |        //
   52|       |        // Thus we may allow convert_masked_utf8_to_utf16 to process
   53|       |        // more bytes at a time under a fast-path mode where 16 bytes
   54|       |        // are consumed at once (e.g., when encountering ASCII).
   55|   616k|        size_t consumed = convert_masked_utf8_to_utf16<endian>(
   56|   616k|            input + pos, utf8_end_of_code_point_mask, utf16_output);
   57|   616k|        pos += consumed;
   58|   616k|        utf8_end_of_code_point_mask >>= consumed;
   59|   616k|      }
   60|       |      // At this point there may remain between 0 and 12 bytes in the
   61|       |      // 64-byte block. These bytes will be processed again. So we have an
   62|       |      // 80% efficiency (in the worst case). In practice we expect an
   63|       |      // 85% to 90% efficiency.
   64|  71.0k|    }
   65|   453k|  }
   66|    518|  utf16_output += scalar::utf8_to_utf16::convert_valid<endian>(
   67|    518|      input + pos, size - pos, utf16_output);
   68|    518|  return utf16_output - start;
   69|    518|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1613convert_validILNS_10endiannessE0EEEmPKcmPDs:
   10|    380|                                         char16_t *utf16_output) noexcept {
   11|       |  // The implementation is not specific to haswell and should be moved to the
   12|       |  // generic directory.
   13|    380|  size_t pos = 0;
   14|    380|  char16_t *start{utf16_output};
   15|    380|  const size_t safety_margin = 16; // to avoid overruns!
   16|   451k|  while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (16:10): [True: 451k, False: 380]
  ------------------
   17|       |    // this loop could be unrolled further. For example, we could process the
   18|       |    // mask far more than 64 bytes.
   19|   451k|    simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
   20|   451k|    if (in.is_ascii()) {
  ------------------
  |  Branch (20:9): [True: 304k, False: 146k]
  ------------------
   21|   304k|      in.store_ascii_as_utf16<endian>(utf16_output);
   22|   304k|      utf16_output += 64;
   23|   304k|      pos += 64;
   24|   304k|    } else {
   25|       |      // Slow path. We hope that the compiler will recognize that this is a slow
   26|       |      // path. Anything that is not a continuation mask is a 'leading byte',
   27|       |      // that is, the start of a new code point.
   28|   146k|      uint64_t utf8_continuation_mask = in.lt(-65 + 1);
   29|       |      // -65 is 0b10111111 in two-complement's, so largest possible continuation
   30|       |      // byte
   31|   146k|      uint64_t utf8_leading_mask = ~utf8_continuation_mask;
   32|       |      // The *start* of code points is not so useful, rather, we want the *end*
   33|       |      // of code points.
   34|   146k|      uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
   35|       |      // We process in blocks of up to 12 bytes except possibly
   36|       |      // for fast paths which may process up to 16 bytes. For the
   37|       |      // slow path to work, we should have at least 12 input bytes left.
   38|   146k|      size_t max_starting_point = (pos + 64) - 12;
   39|       |      // Next loop is going to run at least five times when using solely
   40|       |      // the slow/regular path, and at least four times if there are fast paths.
   41|  1.45M|      while (pos < max_starting_point) {
  ------------------
  |  Branch (41:14): [True: 1.30M, False: 146k]
  ------------------
   42|       |        // Performance note: our ability to compute 'consumed' and
   43|       |        // then shift and recompute is critical. If there is a
   44|       |        // latency of, say, 4 cycles on getting 'consumed', then
   45|       |        // the inner loop might have a total latency of about 6 cycles.
   46|       |        // Yet we process between 6 to 12 inputs bytes, thus we get
   47|       |        // a speed limit between 1 cycle/byte and 0.5 cycle/byte
   48|       |        // for this section of the code. Hence, there is a limit
   49|       |        // to how much we can further increase this latency before
   50|       |        // it seriously harms performance.
   51|       |        //
   52|       |        // Thus we may allow convert_masked_utf8_to_utf16 to process
   53|       |        // more bytes at a time under a fast-path mode where 16 bytes
   54|       |        // are consumed at once (e.g., when encountering ASCII).
   55|  1.30M|        size_t consumed = convert_masked_utf8_to_utf16<endian>(
   56|  1.30M|            input + pos, utf8_end_of_code_point_mask, utf16_output);
   57|  1.30M|        pos += consumed;
   58|  1.30M|        utf8_end_of_code_point_mask >>= consumed;
   59|  1.30M|      }
   60|       |      // At this point there may remain between 0 and 12 bytes in the
   61|       |      // 64-byte block. These bytes will be processed again. So we have an
   62|       |      // 80% efficiency (in the worst case). In practice we expect an
   63|       |      // 85% to 90% efficiency.
   64|   146k|    }
   65|   451k|  }
   66|    380|  utf16_output += scalar::utf8_to_utf16::convert_valid<endian>(
   67|    380|      input + pos, size - pos, utf16_output);
   68|    380|  return utf16_output - start;
   69|    380|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1613convert_validILNS_10endiannessE1EEEmPKcmPDs:
   10|    518|                                         char16_t *utf16_output) noexcept {
   11|       |  // The implementation is not specific to haswell and should be moved to the
   12|       |  // generic directory.
   13|    518|  size_t pos = 0;
   14|    518|  char16_t *start{utf16_output};
   15|    518|  const size_t safety_margin = 16; // to avoid overruns!
   16|   454k|  while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (16:10): [True: 453k, False: 518]
  ------------------
   17|       |    // this loop could be unrolled further. For example, we could process the
   18|       |    // mask far more than 64 bytes.
   19|   453k|    simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
   20|   453k|    if (in.is_ascii()) {
  ------------------
  |  Branch (20:9): [True: 382k, False: 71.0k]
  ------------------
   21|   382k|      in.store_ascii_as_utf16<endian>(utf16_output);
   22|   382k|      utf16_output += 64;
   23|   382k|      pos += 64;
   24|   382k|    } else {
   25|       |      // Slow path. We hope that the compiler will recognize that this is a slow
   26|       |      // path. Anything that is not a continuation mask is a 'leading byte',
   27|       |      // that is, the start of a new code point.
   28|  71.0k|      uint64_t utf8_continuation_mask = in.lt(-65 + 1);
   29|       |      // -65 is 0b10111111 in two-complement's, so largest possible continuation
   30|       |      // byte
   31|  71.0k|      uint64_t utf8_leading_mask = ~utf8_continuation_mask;
   32|       |      // The *start* of code points is not so useful, rather, we want the *end*
   33|       |      // of code points.
   34|  71.0k|      uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
   35|       |      // We process in blocks of up to 12 bytes except possibly
   36|       |      // for fast paths which may process up to 16 bytes. For the
   37|       |      // slow path to work, we should have at least 12 input bytes left.
   38|  71.0k|      size_t max_starting_point = (pos + 64) - 12;
   39|       |      // Next loop is going to run at least five times when using solely
   40|       |      // the slow/regular path, and at least four times if there are fast paths.
   41|   687k|      while (pos < max_starting_point) {
  ------------------
  |  Branch (41:14): [True: 616k, False: 71.0k]
  ------------------
   42|       |        // Performance note: our ability to compute 'consumed' and
   43|       |        // then shift and recompute is critical. If there is a
   44|       |        // latency of, say, 4 cycles on getting 'consumed', then
   45|       |        // the inner loop might have a total latency of about 6 cycles.
   46|       |        // Yet we process between 6 to 12 inputs bytes, thus we get
   47|       |        // a speed limit between 1 cycle/byte and 0.5 cycle/byte
   48|       |        // for this section of the code. Hence, there is a limit
   49|       |        // to how much we can further increase this latency before
   50|       |        // it seriously harms performance.
   51|       |        //
   52|       |        // Thus we may allow convert_masked_utf8_to_utf16 to process
   53|       |        // more bytes at a time under a fast-path mode where 16 bytes
   54|       |        // are consumed at once (e.g., when encountering ASCII).
   55|   616k|        size_t consumed = convert_masked_utf8_to_utf16<endian>(
   56|   616k|            input + pos, utf8_end_of_code_point_mask, utf16_output);
   57|   616k|        pos += consumed;
   58|   616k|        utf8_end_of_code_point_mask >>= consumed;
   59|   616k|      }
   60|       |      // At this point there may remain between 0 and 12 bytes in the
   61|       |      // 64-byte block. These bytes will be processed again. So we have an
   62|       |      // 80% efficiency (in the worst case). In practice we expect an
   63|       |      // 85% to 90% efficiency.
   64|  71.0k|    }
   65|   453k|  }
   66|    518|  utf16_output += scalar::utf8_to_utf16::convert_valid<endian>(
   67|    518|      input + pos, size - pos, utf16_output);
   68|    518|  return utf16_output - start;
   69|    518|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3221validating_transcoderC2Ev:
  114|  1.88k|  validating_transcoder() : error(uint8_t(0)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3221validating_transcoder7convertEPKcmPDi:
  161|    956|                                       char32_t *utf32_output) {
  162|    956|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (162:9): [True: 340, False: 616]
  ------------------
  163|    340|      return scalar::utf8_to_utf32::convert(in, size, utf32_output);
  164|    340|    }
  165|    616|    size_t pos = 0;
  166|    616|    char32_t *start{utf32_output};
  167|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  168|       |    // of 8 words when calling convert_masked_utf8_to_utf32. If you skip the
  169|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  170|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  171|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  172|       |    // back from the end counting 16 leading bytes, to give us a good margin.
  173|    616|    size_t leading_byte = 0;
  174|    616|    size_t margin = size;
  175|  7.32k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (175:12): [True: 7.31k, False: 8]
  |  Branch (175:26): [True: 6.70k, False: 608]
  ------------------
  176|  6.70k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  177|  6.70k|    }
  178|       |    // If the input is long enough, then we have that margin-1 is the fourth
  179|       |    // last leading byte.
  180|    616|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  181|   835k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (181:12): [True: 834k, False: 586]
  ------------------
  182|   834k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  183|   834k|      if (input.is_ascii()) {
  ------------------
  |  Branch (183:11): [True: 494k, False: 340k]
  ------------------
  184|   494k|        input.store_ascii_as_utf32(utf32_output);
  185|   494k|        utf32_output += 64;
  186|   494k|        pos += 64;
  187|   494k|      } else {
  188|       |        // you might think that a for-loop would work, but under Visual Studio,
  189|       |        // it is not good enough.
  190|   340k|        static_assert(
  191|   340k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  192|   340k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  193|   340k|            "We support either two or four chunks per 64-byte block.");
  194|   340k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  195|   340k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  196|   340k|          this->check_utf8_bytes(input.chunks[0], zero);
  197|   340k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  198|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  199|       |          this->check_utf8_bytes(input.chunks[0], zero);
  200|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  201|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  202|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  203|       |        }
  204|   340k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  205|   340k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (205:13): [True: 30, False: 340k]
  ------------------
  206|     30|          return 0; // we have an error
  207|     30|        }
  208|   340k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  209|   340k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  210|       |        // We process in blocks of up to 12 bytes except possibly
  211|       |        // for fast paths which may process up to 16 bytes. For the
  212|       |        // slow path to work, we should have at least 12 input bytes left.
  213|   340k|        size_t max_starting_point = (pos + 64) - 12;
  214|       |        // Next loop is going to run at least five times.
  215|  3.37M|        while (pos < max_starting_point) {
  ------------------
  |  Branch (215:16): [True: 3.03M, False: 340k]
  ------------------
  216|       |          // Performance note: our ability to compute 'consumed' and
  217|       |          // then shift and recompute is critical. If there is a
  218|       |          // latency of, say, 4 cycles on getting 'consumed', then
  219|       |          // the inner loop might have a total latency of about 6 cycles.
  220|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  221|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  222|       |          // for this section of the code. Hence, there is a limit
  223|       |          // to how much we can further increase this latency before
  224|       |          // it seriously harms performance.
  225|  3.03M|          size_t consumed = convert_masked_utf8_to_utf32(
  226|  3.03M|              in + pos, utf8_end_of_code_point_mask, utf32_output);
  227|  3.03M|          pos += consumed;
  228|  3.03M|          utf8_end_of_code_point_mask >>= consumed;
  229|  3.03M|        }
  230|       |        // At this point there may remain between 0 and 12 bytes in the
  231|       |        // 64-byte block. These bytes will be processed again. So we have an
  232|       |        // 80% efficiency (in the worst case). In practice we expect an
  233|       |        // 85% to 90% efficiency.
  234|   340k|      }
  235|   834k|    }
  236|    586|    if (errors()) {
  ------------------
  |  Branch (236:9): [True: 322, False: 264]
  ------------------
  237|    322|      return 0;
  238|    322|    }
  239|    264|    const size_t ascii = convert_ascii_tail(in, pos, size, utf32_output);
  240|    264|    pos += ascii;
  241|    264|    utf32_output += ascii;
  242|    264|    if (pos == size) {
  ------------------
  |  Branch (242:9): [True: 60, False: 204]
  ------------------
  243|     60|      return utf32_output - start;
  244|     60|    }
  245|    204|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|    204|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
  246|    204|    size_t howmany =
  247|    204|        scalar::utf8_to_utf32::convert(in + pos, size - pos, utf32_output);
  248|    204|    if (howmany == 0) {
  ------------------
  |  Branch (248:9): [True: 152, False: 52]
  ------------------
  249|    152|      return 0;
  250|    152|    }
  251|     52|    return utf32_output + howmany - start;
  252|    204|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3221validating_transcoder16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  119|   814k|                                              const simd8<uint8_t> prev_input) {
  120|       |    // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+
  121|       |    // lead bytes (2, 3, 4-byte leads become large positive numbers instead of
  122|       |    // small negative numbers)
  123|   814k|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  124|   814k|    simd8<uint8_t> sc = check_special_cases(input, prev1);
  125|   814k|    this->error |= check_multibyte_lengths(input, prev_input, sc);
  126|   814k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3219check_special_casesENS1_4simd5simd8IhEES5_:
    8|   814k|check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
    9|       |  // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
   10|       |  // Bit 1 = Too Long (ASCII followed by continuation)
   11|       |  // Bit 2 = Overlong 3-byte
   12|       |  // Bit 4 = Surrogate
   13|       |  // Bit 5 = Overlong 2-byte
   14|       |  // Bit 7 = Two Continuations
   15|   814k|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   16|       |                                               // 11______ 11______
   17|   814k|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   18|   814k|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   19|   814k|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   20|   814k|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   21|   814k|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   22|   814k|  constexpr const uint8_t TOO_LARGE = 1 << 3;  // 11110100 1001____
   23|       |                                               // 11110100 101_____
   24|       |                                               // 11110101 1001____
   25|       |                                               // 11110101 101_____
   26|       |                                               // 1111011_ 1001____
   27|       |                                               // 1111011_ 101_____
   28|       |                                               // 11111___ 1001____
   29|       |                                               // 11111___ 101_____
   30|   814k|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   31|       |  // 11110101 1000____
   32|       |  // 1111011_ 1000____
   33|       |  // 11111___ 1000____
   34|   814k|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   35|       |
   36|   814k|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   37|       |      // 0_______ ________ <ASCII in byte 1>
   38|   814k|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   39|   814k|      TOO_LONG,
   40|       |      // 10______ ________ <continuation in byte 1>
   41|   814k|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   42|       |      // 1100____ ________ <two byte lead in byte 1>
   43|   814k|      TOO_SHORT | OVERLONG_2,
   44|       |      // 1101____ ________ <two byte lead in byte 1>
   45|   814k|      TOO_SHORT,
   46|       |      // 1110____ ________ <three byte lead in byte 1>
   47|   814k|      TOO_SHORT | OVERLONG_3 | SURROGATE,
   48|       |      // 1111____ ________ <four+ byte lead in byte 1>
   49|   814k|      TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4);
   50|   814k|  constexpr const uint8_t CARRY =
   51|   814k|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   52|   814k|  const simd8<uint8_t> byte_1_low =
   53|   814k|      (prev1 & 0x0F)
   54|   814k|          .lookup_16<uint8_t>(
   55|       |              // ____0000 ________
   56|   814k|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   57|       |              // ____0001 ________
   58|   814k|              CARRY | OVERLONG_2,
   59|       |              // ____001_ ________
   60|   814k|              CARRY, CARRY,
   61|       |
   62|       |              // ____0100 ________
   63|   814k|              CARRY | TOO_LARGE,
   64|       |              // ____0101 ________
   65|   814k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   66|       |              // ____011_ ________
   67|   814k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   68|   814k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   69|       |
   70|       |              // ____1___ ________
   71|   814k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   72|   814k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   73|   814k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   74|   814k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   75|   814k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   76|       |              // ____1101 ________
   77|   814k|              CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
   78|   814k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   79|   814k|              CARRY | TOO_LARGE | TOO_LARGE_1000);
   80|   814k|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   81|       |      // ________ 0_______ <ASCII in byte 2>
   82|   814k|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   83|   814k|      TOO_SHORT, TOO_SHORT,
   84|       |
   85|       |      // ________ 1000____
   86|   814k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   87|   814k|          OVERLONG_4,
   88|       |      // ________ 1001____
   89|   814k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   90|       |      // ________ 101_____
   91|   814k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   92|   814k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   93|       |
   94|       |      // ________ 11______
   95|   814k|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   96|   814k|  return (byte_1_high & byte_1_low & byte_2_high);
   97|   814k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3223check_multibyte_lengthsENS1_4simd5simd8IhEES5_S5_:
  101|   814k|                        const simd8<uint8_t> sc) {
  102|   814k|  simd8<uint8_t> prev2 = input.prev<2>(prev_input);
  103|   814k|  simd8<uint8_t> prev3 = input.prev<3>(prev_input);
  104|   814k|  simd8<uint8_t> must23 =
  105|   814k|      simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
  106|   814k|  simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
  107|   814k|  return must23_80 ^ sc;
  108|   814k|}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3221validating_transcoder6errorsEv:
  355|  67.7k|  simdutf_really_inline bool errors() const {
  356|  67.7k|    return this->error.any_bits_set_anywhere();
  357|  67.7k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3221validating_transcoder18convert_ascii_tailEPKcmmPDi:
  130|    788|                                                  char32_t *output) {
  131|       |    // A cheap test on the first bytes avoids any SIMD work for most non-ASCII
  132|       |    // tails.
  133|    788|    if (size - pos >= 8) {
  ------------------
  |  Branch (133:9): [True: 788, False: 0]
  ------------------
  134|    788|      uint64_t first_bytes;
  135|    788|      std::memcpy(&first_bytes, in + pos, 8);
  136|    788|      if (first_bytes & 0x8080808080808080) {
  ------------------
  |  Branch (136:11): [True: 190, False: 598]
  ------------------
  137|    190|        return 0;
  138|    190|      }
  139|    788|    }
  140|    598|    const size_t start = pos;
  141|    682|    while (pos + 64 <= size) {
  ------------------
  |  Branch (141:12): [True: 260, False: 422]
  ------------------
  142|    260|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  143|    260|      if (!input.is_ascii()) {
  ------------------
  |  Branch (143:11): [True: 176, False: 84]
  ------------------
  144|    176|        return pos - start;
  145|    176|      }
  146|     84|      input.store_ascii_as_utf32(output + (pos - start));
  147|     84|      pos += 64;
  148|     84|    }
  149|    422|    if (pos == size || size < 64) {
  ------------------
  |  Branch (149:9): [True: 28, False: 394]
  |  Branch (149:24): [True: 0, False: 394]
  ------------------
  150|     28|      return pos - start;
  151|     28|    }
  152|    394|    simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + size - 64));
  153|    394|    if (!input.is_ascii()) {
  ------------------
  |  Branch (153:9): [True: 262, False: 132]
  ------------------
  154|    262|      return pos - start;
  155|    262|    }
  156|    132|    input.store_ascii_as_utf32(output + (pos - start) - (pos - (size - 64)));
  157|    132|    return size - start;
  158|    394|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3221validating_transcoder19convert_with_errorsEPKcmPDi:
  255|    926|                                                   char32_t *utf32_output) {
  256|    926|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (256:9): [True: 312, False: 614]
  ------------------
  257|    312|      return scalar::utf8_to_utf32::convert_with_errors(in, size, utf32_output);
  258|    312|    }
  259|    614|    size_t pos = 0;
  260|    614|    char32_t *start{utf32_output};
  261|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  262|       |    // of 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the
  263|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  264|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  265|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  266|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  267|    614|    size_t leading_byte = 0;
  268|    614|    size_t margin = size;
  269|  8.20k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (269:12): [True: 8.19k, False: 18]
  |  Branch (269:26): [True: 7.59k, False: 596]
  ------------------
  270|  7.59k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  271|  7.59k|    }
  272|       |    // If the input is long enough, then we have that margin-1 is the fourth
  273|       |    // last leading byte.
  274|    614|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  275|   573k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (275:12): [True: 573k, False: 524]
  ------------------
  276|   573k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  277|   573k|      if (input.is_ascii()) {
  ------------------
  |  Branch (277:11): [True: 505k, False: 67.1k]
  ------------------
  278|   505k|        input.store_ascii_as_utf32(utf32_output);
  279|   505k|        utf32_output += 64;
  280|   505k|        pos += 64;
  281|   505k|      } else {
  282|       |        // you might think that a for-loop would work, but under Visual Studio,
  283|       |        // it is not good enough.
  284|  67.1k|        static_assert(
  285|  67.1k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  286|  67.1k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  287|  67.1k|            "We support either two or four chunks per 64-byte block.");
  288|  67.1k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  289|  67.1k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  290|  67.1k|          this->check_utf8_bytes(input.chunks[0], zero);
  291|  67.1k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  292|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  293|       |          this->check_utf8_bytes(input.chunks[0], zero);
  294|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  295|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  296|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  297|       |        }
  298|  67.1k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  299|  67.1k|        if (errors() || (utf8_continuation_mask & 1)) {
  ------------------
  |  Branch (299:13): [True: 90, False: 67.0k]
  |  Branch (299:25): [True: 0, False: 67.0k]
  ------------------
  300|     90|          SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|     90|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
  301|     90|          result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(
  302|     90|              pos, in + pos, size - pos, utf32_output);
  303|     90|          res.count += pos;
  304|     90|          return res;
  305|     90|        }
  306|  67.0k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  307|  67.0k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  308|       |        // We process in blocks of up to 12 bytes except possibly
  309|       |        // for fast paths which may process up to 16 bytes. For the
  310|       |        // slow path to work, we should have at least 12 input bytes left.
  311|  67.0k|        size_t max_starting_point = (pos + 64) - 12;
  312|       |        // Next loop is going to run at least five times.
  313|   607k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (313:16): [True: 540k, False: 67.0k]
  ------------------
  314|       |          // Performance note: our ability to compute 'consumed' and
  315|       |          // then shift and recompute is critical. If there is a
  316|       |          // latency of, say, 4 cycles on getting 'consumed', then
  317|       |          // the inner loop might have a total latency of about 6 cycles.
  318|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  319|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  320|       |          // for this section of the code. Hence, there is a limit
  321|       |          // to how much we can further increase this latency before
  322|       |          // it seriously harms performance.
  323|   540k|          size_t consumed = convert_masked_utf8_to_utf32(
  324|   540k|              in + pos, utf8_end_of_code_point_mask, utf32_output);
  325|   540k|          pos += consumed;
  326|   540k|          utf8_end_of_code_point_mask >>= consumed;
  327|   540k|        }
  328|       |        // At this point there may remain between 0 and 12 bytes in the
  329|       |        // 64-byte block. These bytes will be processed again. So we have an
  330|       |        // 80% efficiency (in the worst case). In practice we expect an
  331|       |        // 85% to 90% efficiency.
  332|  67.0k|      }
  333|   573k|    }
  334|    524|    const size_t ascii = convert_ascii_tail(in, pos, size, utf32_output);
  335|    524|    pos += ascii;
  336|    524|    utf32_output += ascii;
  337|    524|    if (pos == size) {
  ------------------
  |  Branch (337:9): [True: 100, False: 424]
  ------------------
  338|    100|      return result(error_code::SUCCESS, utf32_output - start);
  339|    100|    }
  340|       |    // rewind_and_convert_with_errors will seek a potential error from in+pos
  341|       |    // onward, with the ability to go back up to pos bytes, and read size-pos
  342|       |    // bytes forward.
  343|    424|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|    424|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
  344|    424|    result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(
  345|    424|        pos, in + pos, size - pos, utf32_output);
  346|    424|    if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (346:9): [True: 296, False: 128]
  ------------------
  347|    296|      res.count += pos;
  348|    296|      return res;
  349|    296|    }
  350|       |    // In case of success, we want the number of words written
  351|    128|    utf32_output += res.count;
  352|    128|    return result(error_code::SUCCESS, utf32_output - start);
  353|    424|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3221validating_transcoderC2Ev:
  114|  1.88k|  validating_transcoder() : error(uint8_t(0)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3221validating_transcoder7convertEPKcmPDi:
  161|    956|                                       char32_t *utf32_output) {
  162|    956|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (162:9): [True: 340, False: 616]
  ------------------
  163|    340|      return scalar::utf8_to_utf32::convert(in, size, utf32_output);
  164|    340|    }
  165|    616|    size_t pos = 0;
  166|    616|    char32_t *start{utf32_output};
  167|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  168|       |    // of 8 words when calling convert_masked_utf8_to_utf32. If you skip the
  169|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  170|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  171|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  172|       |    // back from the end counting 16 leading bytes, to give us a good margin.
  173|    616|    size_t leading_byte = 0;
  174|    616|    size_t margin = size;
  175|  7.32k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (175:12): [True: 7.31k, False: 8]
  |  Branch (175:26): [True: 6.70k, False: 608]
  ------------------
  176|  6.70k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  177|  6.70k|    }
  178|       |    // If the input is long enough, then we have that margin-1 is the fourth
  179|       |    // last leading byte.
  180|    616|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  181|   835k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (181:12): [True: 834k, False: 586]
  ------------------
  182|   834k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  183|   834k|      if (input.is_ascii()) {
  ------------------
  |  Branch (183:11): [True: 494k, False: 340k]
  ------------------
  184|   494k|        input.store_ascii_as_utf32(utf32_output);
  185|   494k|        utf32_output += 64;
  186|   494k|        pos += 64;
  187|   494k|      } else {
  188|       |        // you might think that a for-loop would work, but under Visual Studio,
  189|       |        // it is not good enough.
  190|   340k|        static_assert(
  191|   340k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  192|   340k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  193|   340k|            "We support either two or four chunks per 64-byte block.");
  194|   340k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  195|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  196|       |          this->check_utf8_bytes(input.chunks[0], zero);
  197|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  198|   340k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  199|   340k|          this->check_utf8_bytes(input.chunks[0], zero);
  200|   340k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  201|   340k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  202|   340k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  203|   340k|        }
  204|   340k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  205|   340k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (205:13): [True: 30, False: 340k]
  ------------------
  206|     30|          return 0; // we have an error
  207|     30|        }
  208|   340k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  209|   340k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  210|       |        // We process in blocks of up to 12 bytes except possibly
  211|       |        // for fast paths which may process up to 16 bytes. For the
  212|       |        // slow path to work, we should have at least 12 input bytes left.
  213|   340k|        size_t max_starting_point = (pos + 64) - 12;
  214|       |        // Next loop is going to run at least five times.
  215|  3.37M|        while (pos < max_starting_point) {
  ------------------
  |  Branch (215:16): [True: 3.03M, False: 340k]
  ------------------
  216|       |          // Performance note: our ability to compute 'consumed' and
  217|       |          // then shift and recompute is critical. If there is a
  218|       |          // latency of, say, 4 cycles on getting 'consumed', then
  219|       |          // the inner loop might have a total latency of about 6 cycles.
  220|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  221|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  222|       |          // for this section of the code. Hence, there is a limit
  223|       |          // to how much we can further increase this latency before
  224|       |          // it seriously harms performance.
  225|  3.03M|          size_t consumed = convert_masked_utf8_to_utf32(
  226|  3.03M|              in + pos, utf8_end_of_code_point_mask, utf32_output);
  227|  3.03M|          pos += consumed;
  228|  3.03M|          utf8_end_of_code_point_mask >>= consumed;
  229|  3.03M|        }
  230|       |        // At this point there may remain between 0 and 12 bytes in the
  231|       |        // 64-byte block. These bytes will be processed again. So we have an
  232|       |        // 80% efficiency (in the worst case). In practice we expect an
  233|       |        // 85% to 90% efficiency.
  234|   340k|      }
  235|   834k|    }
  236|    586|    if (errors()) {
  ------------------
  |  Branch (236:9): [True: 322, False: 264]
  ------------------
  237|    322|      return 0;
  238|    322|    }
  239|    264|    const size_t ascii = convert_ascii_tail(in, pos, size, utf32_output);
  240|    264|    pos += ascii;
  241|    264|    utf32_output += ascii;
  242|    264|    if (pos == size) {
  ------------------
  |  Branch (242:9): [True: 60, False: 204]
  ------------------
  243|     60|      return utf32_output - start;
  244|     60|    }
  245|    204|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   16|    204|#define SIMDUTF_VZEROUPPER() (void)0
  ------------------
  246|    204|    size_t howmany =
  247|    204|        scalar::utf8_to_utf32::convert(in + pos, size - pos, utf32_output);
  248|    204|    if (howmany == 0) {
  ------------------
  |  Branch (248:9): [True: 152, False: 52]
  ------------------
  249|    152|      return 0;
  250|    152|    }
  251|     52|    return utf32_output + howmany - start;
  252|    204|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3221validating_transcoder16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  119|  1.62M|                                              const simd8<uint8_t> prev_input) {
  120|       |    // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+
  121|       |    // lead bytes (2, 3, 4-byte leads become large positive numbers instead of
  122|       |    // small negative numbers)
  123|  1.62M|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  124|  1.62M|    simd8<uint8_t> sc = check_special_cases(input, prev1);
  125|  1.62M|    this->error |= check_multibyte_lengths(input, prev_input, sc);
  126|  1.62M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3219check_special_casesENS1_4simd5simd8IhEES5_:
    8|  1.62M|check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
    9|       |  // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
   10|       |  // Bit 1 = Too Long (ASCII followed by continuation)
   11|       |  // Bit 2 = Overlong 3-byte
   12|       |  // Bit 4 = Surrogate
   13|       |  // Bit 5 = Overlong 2-byte
   14|       |  // Bit 7 = Two Continuations
   15|  1.62M|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   16|       |                                               // 11______ 11______
   17|  1.62M|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   18|  1.62M|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   19|  1.62M|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   20|  1.62M|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   21|  1.62M|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   22|  1.62M|  constexpr const uint8_t TOO_LARGE = 1 << 3;  // 11110100 1001____
   23|       |                                               // 11110100 101_____
   24|       |                                               // 11110101 1001____
   25|       |                                               // 11110101 101_____
   26|       |                                               // 1111011_ 1001____
   27|       |                                               // 1111011_ 101_____
   28|       |                                               // 11111___ 1001____
   29|       |                                               // 11111___ 101_____
   30|  1.62M|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   31|       |  // 11110101 1000____
   32|       |  // 1111011_ 1000____
   33|       |  // 11111___ 1000____
   34|  1.62M|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   35|       |
   36|  1.62M|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   37|       |      // 0_______ ________ <ASCII in byte 1>
   38|  1.62M|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   39|  1.62M|      TOO_LONG,
   40|       |      // 10______ ________ <continuation in byte 1>
   41|  1.62M|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   42|       |      // 1100____ ________ <two byte lead in byte 1>
   43|  1.62M|      TOO_SHORT | OVERLONG_2,
   44|       |      // 1101____ ________ <two byte lead in byte 1>
   45|  1.62M|      TOO_SHORT,
   46|       |      // 1110____ ________ <three byte lead in byte 1>
   47|  1.62M|      TOO_SHORT | OVERLONG_3 | SURROGATE,
   48|       |      // 1111____ ________ <four+ byte lead in byte 1>
   49|  1.62M|      TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4);
   50|  1.62M|  constexpr const uint8_t CARRY =
   51|  1.62M|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   52|  1.62M|  const simd8<uint8_t> byte_1_low =
   53|  1.62M|      (prev1 & 0x0F)
   54|  1.62M|          .lookup_16<uint8_t>(
   55|       |              // ____0000 ________
   56|  1.62M|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   57|       |              // ____0001 ________
   58|  1.62M|              CARRY | OVERLONG_2,
   59|       |              // ____001_ ________
   60|  1.62M|              CARRY, CARRY,
   61|       |
   62|       |              // ____0100 ________
   63|  1.62M|              CARRY | TOO_LARGE,
   64|       |              // ____0101 ________
   65|  1.62M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   66|       |              // ____011_ ________
   67|  1.62M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   68|  1.62M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   69|       |
   70|       |              // ____1___ ________
   71|  1.62M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   72|  1.62M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   73|  1.62M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   74|  1.62M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   75|  1.62M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   76|       |              // ____1101 ________
   77|  1.62M|              CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
   78|  1.62M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   79|  1.62M|              CARRY | TOO_LARGE | TOO_LARGE_1000);
   80|  1.62M|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   81|       |      // ________ 0_______ <ASCII in byte 2>
   82|  1.62M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   83|  1.62M|      TOO_SHORT, TOO_SHORT,
   84|       |
   85|       |      // ________ 1000____
   86|  1.62M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   87|  1.62M|          OVERLONG_4,
   88|       |      // ________ 1001____
   89|  1.62M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   90|       |      // ________ 101_____
   91|  1.62M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   92|  1.62M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   93|       |
   94|       |      // ________ 11______
   95|  1.62M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   96|  1.62M|  return (byte_1_high & byte_1_low & byte_2_high);
   97|  1.62M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3223check_multibyte_lengthsENS1_4simd5simd8IhEES5_S5_:
  101|  1.62M|                        const simd8<uint8_t> sc) {
  102|  1.62M|  simd8<uint8_t> prev2 = input.prev<2>(prev_input);
  103|  1.62M|  simd8<uint8_t> prev3 = input.prev<3>(prev_input);
  104|  1.62M|  simd8<uint8_t> must23 =
  105|  1.62M|      simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
  106|  1.62M|  simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
  107|  1.62M|  return must23_80 ^ sc;
  108|  1.62M|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3221validating_transcoder6errorsEv:
  355|  67.7k|  simdutf_really_inline bool errors() const {
  356|  67.7k|    return this->error.any_bits_set_anywhere();
  357|  67.7k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3221validating_transcoder18convert_ascii_tailEPKcmmPDi:
  130|    788|                                                  char32_t *output) {
  131|       |    // A cheap test on the first bytes avoids any SIMD work for most non-ASCII
  132|       |    // tails.
  133|    788|    if (size - pos >= 8) {
  ------------------
  |  Branch (133:9): [True: 788, False: 0]
  ------------------
  134|    788|      uint64_t first_bytes;
  135|    788|      std::memcpy(&first_bytes, in + pos, 8);
  136|    788|      if (first_bytes & 0x8080808080808080) {
  ------------------
  |  Branch (136:11): [True: 190, False: 598]
  ------------------
  137|    190|        return 0;
  138|    190|      }
  139|    788|    }
  140|    598|    const size_t start = pos;
  141|    682|    while (pos + 64 <= size) {
  ------------------
  |  Branch (141:12): [True: 260, False: 422]
  ------------------
  142|    260|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  143|    260|      if (!input.is_ascii()) {
  ------------------
  |  Branch (143:11): [True: 176, False: 84]
  ------------------
  144|    176|        return pos - start;
  145|    176|      }
  146|     84|      input.store_ascii_as_utf32(output + (pos - start));
  147|     84|      pos += 64;
  148|     84|    }
  149|    422|    if (pos == size || size < 64) {
  ------------------
  |  Branch (149:9): [True: 28, False: 394]
  |  Branch (149:24): [True: 0, False: 394]
  ------------------
  150|     28|      return pos - start;
  151|     28|    }
  152|    394|    simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + size - 64));
  153|    394|    if (!input.is_ascii()) {
  ------------------
  |  Branch (153:9): [True: 262, False: 132]
  ------------------
  154|    262|      return pos - start;
  155|    262|    }
  156|    132|    input.store_ascii_as_utf32(output + (pos - start) - (pos - (size - 64)));
  157|    132|    return size - start;
  158|    394|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3221validating_transcoder19convert_with_errorsEPKcmPDi:
  255|    926|                                                   char32_t *utf32_output) {
  256|    926|    if (size < 64) { // too short for the main loop
  ------------------
  |  Branch (256:9): [True: 312, False: 614]
  ------------------
  257|    312|      return scalar::utf8_to_utf32::convert_with_errors(in, size, utf32_output);
  258|    312|    }
  259|    614|    size_t pos = 0;
  260|    614|    char32_t *start{utf32_output};
  261|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  262|       |    // of 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the
  263|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  264|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  265|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  266|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  267|    614|    size_t leading_byte = 0;
  268|    614|    size_t margin = size;
  269|  8.20k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (269:12): [True: 8.19k, False: 18]
  |  Branch (269:26): [True: 7.59k, False: 596]
  ------------------
  270|  7.59k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  271|  7.59k|    }
  272|       |    // If the input is long enough, then we have that margin-1 is the fourth
  273|       |    // last leading byte.
  274|    614|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  275|   573k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (275:12): [True: 573k, False: 524]
  ------------------
  276|   573k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  277|   573k|      if (input.is_ascii()) {
  ------------------
  |  Branch (277:11): [True: 505k, False: 67.1k]
  ------------------
  278|   505k|        input.store_ascii_as_utf32(utf32_output);
  279|   505k|        utf32_output += 64;
  280|   505k|        pos += 64;
  281|   505k|      } else {
  282|       |        // you might think that a for-loop would work, but under Visual Studio,
  283|       |        // it is not good enough.
  284|  67.1k|        static_assert(
  285|  67.1k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  286|  67.1k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  287|  67.1k|            "We support either two or four chunks per 64-byte block.");
  288|  67.1k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  289|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  290|       |          this->check_utf8_bytes(input.chunks[0], zero);
  291|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  292|  67.1k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  293|  67.1k|          this->check_utf8_bytes(input.chunks[0], zero);
  294|  67.1k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  295|  67.1k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  296|  67.1k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  297|  67.1k|        }
  298|  67.1k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  299|  67.1k|        if (errors() || (utf8_continuation_mask & 1)) {
  ------------------
  |  Branch (299:13): [True: 90, False: 67.0k]
  |  Branch (299:25): [True: 0, False: 67.0k]
  ------------------
  300|     90|          SIMDUTF_VZEROUPPER();
  ------------------
  |  |   16|     90|#define SIMDUTF_VZEROUPPER() (void)0
  ------------------
  301|     90|          result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(
  302|     90|              pos, in + pos, size - pos, utf32_output);
  303|     90|          res.count += pos;
  304|     90|          return res;
  305|     90|        }
  306|  67.0k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  307|  67.0k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  308|       |        // We process in blocks of up to 12 bytes except possibly
  309|       |        // for fast paths which may process up to 16 bytes. For the
  310|       |        // slow path to work, we should have at least 12 input bytes left.
  311|  67.0k|        size_t max_starting_point = (pos + 64) - 12;
  312|       |        // Next loop is going to run at least five times.
  313|   607k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (313:16): [True: 540k, False: 67.0k]
  ------------------
  314|       |          // Performance note: our ability to compute 'consumed' and
  315|       |          // then shift and recompute is critical. If there is a
  316|       |          // latency of, say, 4 cycles on getting 'consumed', then
  317|       |          // the inner loop might have a total latency of about 6 cycles.
  318|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  319|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  320|       |          // for this section of the code. Hence, there is a limit
  321|       |          // to how much we can further increase this latency before
  322|       |          // it seriously harms performance.
  323|   540k|          size_t consumed = convert_masked_utf8_to_utf32(
  324|   540k|              in + pos, utf8_end_of_code_point_mask, utf32_output);
  325|   540k|          pos += consumed;
  326|   540k|          utf8_end_of_code_point_mask >>= consumed;
  327|   540k|        }
  328|       |        // At this point there may remain between 0 and 12 bytes in the
  329|       |        // 64-byte block. These bytes will be processed again. So we have an
  330|       |        // 80% efficiency (in the worst case). In practice we expect an
  331|       |        // 85% to 90% efficiency.
  332|  67.0k|      }
  333|   573k|    }
  334|    524|    const size_t ascii = convert_ascii_tail(in, pos, size, utf32_output);
  335|    524|    pos += ascii;
  336|    524|    utf32_output += ascii;
  337|    524|    if (pos == size) {
  ------------------
  |  Branch (337:9): [True: 100, False: 424]
  ------------------
  338|    100|      return result(error_code::SUCCESS, utf32_output - start);
  339|    100|    }
  340|       |    // rewind_and_convert_with_errors will seek a potential error from in+pos
  341|       |    // onward, with the ability to go back up to pos bytes, and read size-pos
  342|       |    // bytes forward.
  343|    424|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   16|    424|#define SIMDUTF_VZEROUPPER() (void)0
  ------------------
  344|    424|    result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(
  345|    424|        pos, in + pos, size - pos, utf32_output);
  346|    424|    if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (346:9): [True: 296, False: 128]
  ------------------
  347|    296|      res.count += pos;
  348|    296|      return res;
  349|    296|    }
  350|       |    // In case of success, we want the number of words written
  351|    128|    utf32_output += res.count;
  352|    128|    return result(error_code::SUCCESS, utf32_output - start);
  353|    424|  }

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3213convert_validEPKcmPDi:
    9|    506|                                         char32_t *utf32_output) noexcept {
   10|    506|  size_t pos = 0;
   11|    506|  char32_t *start{utf32_output};
   12|    506|  const size_t safety_margin = 16; // to avoid overruns!
   13|   416k|  while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (13:10): [True: 416k, False: 506]
  ------------------
   14|   416k|    simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
   15|   416k|    if (in.is_ascii()) {
  ------------------
  |  Branch (15:9): [True: 380k, False: 36.1k]
  ------------------
   16|   380k|      in.store_ascii_as_utf32(utf32_output);
   17|   380k|      utf32_output += 64;
   18|   380k|      pos += 64;
   19|   380k|    } else {
   20|       |      // -65 is 0b10111111 in two-complement's, so largest possible continuation
   21|       |      // byte
   22|  36.1k|      uint64_t utf8_continuation_mask = in.lt(-65 + 1);
   23|  36.1k|      uint64_t utf8_leading_mask = ~utf8_continuation_mask;
   24|  36.1k|      uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
   25|  36.1k|      size_t max_starting_point = (pos + 64) - 12;
   26|   347k|      while (pos < max_starting_point) {
  ------------------
  |  Branch (26:14): [True: 311k, False: 36.1k]
  ------------------
   27|   311k|        size_t consumed = convert_masked_utf8_to_utf32(
   28|   311k|            input + pos, utf8_end_of_code_point_mask, utf32_output);
   29|   311k|        pos += consumed;
   30|   311k|        utf8_end_of_code_point_mask >>= consumed;
   31|   311k|      }
   32|  36.1k|    }
   33|   416k|  }
   34|    506|  utf32_output += scalar::utf8_to_utf32::convert_valid(input + pos, size - pos,
   35|    506|                                                       utf32_output);
   36|    506|  return utf32_output - start;
   37|    506|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3213convert_validEPKcmPDi:
    9|    506|                                         char32_t *utf32_output) noexcept {
   10|    506|  size_t pos = 0;
   11|    506|  char32_t *start{utf32_output};
   12|    506|  const size_t safety_margin = 16; // to avoid overruns!
   13|   416k|  while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (13:10): [True: 416k, False: 506]
  ------------------
   14|   416k|    simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
   15|   416k|    if (in.is_ascii()) {
  ------------------
  |  Branch (15:9): [True: 380k, False: 36.1k]
  ------------------
   16|   380k|      in.store_ascii_as_utf32(utf32_output);
   17|   380k|      utf32_output += 64;
   18|   380k|      pos += 64;
   19|   380k|    } else {
   20|       |      // -65 is 0b10111111 in two-complement's, so largest possible continuation
   21|       |      // byte
   22|  36.1k|      uint64_t utf8_continuation_mask = in.lt(-65 + 1);
   23|  36.1k|      uint64_t utf8_leading_mask = ~utf8_continuation_mask;
   24|  36.1k|      uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
   25|  36.1k|      size_t max_starting_point = (pos + 64) - 12;
   26|   347k|      while (pos < max_starting_point) {
  ------------------
  |  Branch (26:14): [True: 311k, False: 36.1k]
  ------------------
   27|   311k|        size_t consumed = convert_masked_utf8_to_utf32(
   28|   311k|            input + pos, utf8_end_of_code_point_mask, utf32_output);
   29|   311k|        pos += consumed;
   30|   311k|        utf8_end_of_code_point_mask >>= consumed;
   31|   311k|      }
   32|  36.1k|    }
   33|   416k|  }
   34|    506|  utf32_output += scalar::utf8_to_utf32::convert_valid(input + pos, size - pos,
   35|    506|                                                       utf32_output);
   36|    506|  return utf32_output - start;
   37|    506|}

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

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation21generic_validate_utf8EPKcm:
   58|  4.20k|bool generic_validate_utf8(const char *input, size_t length) {
   59|  4.20k|  return generic_validate_utf8<utf8_checker>(
   60|  4.20k|      reinterpret_cast<const uint8_t *>(input), length);
   61|  4.20k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation21generic_validate_utf8INS2_12utf8_checkerEEEbPKhm:
   46|  4.20k|bool generic_validate_utf8(const uint8_t *input, size_t length) {
   47|  4.20k|  checker c{};
   48|  4.20k|  buf_block_reader<64> reader(input, length);
   49|  3.30M|  while (reader.has_full_block()) {
  ------------------
  |  Branch (49:10): [True: 3.29M, False: 4.20k]
  ------------------
   50|  3.29M|    simd::simd8x64<uint8_t> in(reader.full_block());
   51|  3.29M|    c.check_next_input(in);
   52|  3.29M|    reader.advance();
   53|  3.29M|  }
   54|  4.20k|  check_remainder(c, input, length, reader.block_index());
   55|  4.20k|  return !c.errors();
   56|  4.20k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation15check_remainderINS2_12utf8_checkerEEEvRT_PKhmm:
   15|  7.41k|                                           size_t length, size_t idx) {
   16|  7.41k|  constexpr size_t block = sizeof(simd::simd8x64<uint8_t>);
   17|  7.41k|  constexpr size_t width = sizeof(simd::simd8<uint8_t>);
   18|  7.41k|  static_assert(block == 8 * sizeof(uint64_t), "one mask bit per byte");
   19|  7.41k|  if (length >= block + width) {
  ------------------
  |  Branch (19:7): [True: 2.63k, False: 4.78k]
  ------------------
   20|       |    // The window starts at or before idx: buf_block_reader leaves 1 to 64
   21|       |    // bytes, so 0 <= idx - start < 64.
   22|  2.63k|    const size_t start = length - block;
   23|  2.63k|    const simd::simd8x64<uint8_t> in(input + start);
   24|       |    // Bit i is set if byte start + i is not ASCII, so the bytes from idx on
   25|       |    // are the top length - idx bits. is_ascii() goes first because GCC, up
   26|       |    // to at least GCC 17, builds gteq_unsigned(0x80) from min and compare
   27|       |    // instead of one movemask, which costs ASCII tails about 0.5 ns.
   28|  2.63k|    if (!in.is_ascii() && (in.gteq_unsigned(0x80) >> (idx - start)) != 0) {
  ------------------
  |  Branch (28:9): [True: 1.75k, False: 881]
  |  Branch (28:27): [True: 1.46k, False: 293]
  ------------------
   29|  1.46k|      c.check_overlapping_input(in,
   30|  1.46k|                                simd::simd8<uint8_t>(input + start - width));
   31|  1.46k|    }
   32|  4.78k|  } else if (idx != length) {
  ------------------
  |  Branch (32:14): [True: 4.74k, False: 36]
  ------------------
   33|       |    // Only an empty input leaves no bytes. Its pointer may be null, and
   34|       |    // memcpy from a null pointer is undefined.
   35|  4.74k|    uint8_t buffer[block]{};
   36|  4.74k|    std::memcpy(buffer, input + idx, length - idx);
   37|  4.74k|    c.check_next_input(simd::simd8x64<uint8_t>(buffer));
   38|  4.74k|  }
   39|  7.41k|  c.check_eof();
   40|  7.41k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation33generic_validate_utf8_with_errorsEPKcm:
  104|  4.20k|result generic_validate_utf8_with_errors(const char *input, size_t length) {
  105|  4.20k|  return generic_validate_utf8_with_errors<utf8_checker>(
  106|  4.20k|      reinterpret_cast<const uint8_t *>(input), length);
  107|  4.20k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation33generic_validate_utf8_with_errorsINS2_12utf8_checkerEEENS_6resultEPKhm:
   67|  4.20k|result generic_validate_utf8_with_errors(const uint8_t *input, size_t length) {
   68|  4.20k|  checker c{};
   69|  4.20k|  buf_block_reader<64> reader(input, length);
   70|  4.20k|  size_t count{0};
   71|  2.24M|  while (reader.has_full_block()) {
  ------------------
  |  Branch (71:10): [True: 2.24M, False: 3.20k]
  ------------------
   72|  2.24M|    simd::simd8x64<uint8_t> in(reader.full_block());
   73|  2.24M|    c.check_next_input(in);
   74|  2.24M|    if (c.errors()) {
  ------------------
  |  Branch (74:9): [True: 1.00k, False: 2.24M]
  ------------------
   75|  1.00k|      if (count != 0) {
  ------------------
  |  Branch (75:11): [True: 285, False: 715]
  ------------------
   76|    285|        count--;
   77|    285|      } // Sometimes the error is only detected in the next chunk
   78|  1.00k|      SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|  1.00k|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
   79|  1.00k|      result res = scalar::utf8::rewind_and_validate_with_errors(
   80|  1.00k|          reinterpret_cast<const char *>(input),
   81|  1.00k|          reinterpret_cast<const char *>(input + count), length - count);
   82|  1.00k|      res.count += count;
   83|  1.00k|      return res;
   84|  1.00k|    }
   85|  2.24M|    reader.advance();
   86|  2.24M|    count += 64;
   87|  2.24M|  }
   88|  3.20k|  check_remainder(c, input, length, reader.block_index());
   89|  3.20k|  if (c.errors()) {
  ------------------
  |  Branch (89:7): [True: 1.42k, False: 1.78k]
  ------------------
   90|  1.42k|    if (count != 0) {
  ------------------
  |  Branch (90:9): [True: 510, False: 918]
  ------------------
   91|    510|      count--;
   92|    510|    } // Sometimes the error is only detected in the next chunk
   93|  1.42k|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|  1.42k|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
   94|  1.42k|    result res = scalar::utf8::rewind_and_validate_with_errors(
   95|  1.42k|        reinterpret_cast<const char *>(input),
   96|  1.42k|        reinterpret_cast<const char *>(input) + count, length - count);
   97|  1.42k|    res.count += count;
   98|  1.42k|    return res;
   99|  1.78k|  } else {
  100|  1.78k|    return result(error_code::SUCCESS, length);
  101|  1.78k|  }
  102|  3.20k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation21generic_validate_utf8EPKcm:
   58|  4.20k|bool generic_validate_utf8(const char *input, size_t length) {
   59|  4.20k|  return generic_validate_utf8<utf8_checker>(
   60|  4.20k|      reinterpret_cast<const uint8_t *>(input), length);
   61|  4.20k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation21generic_validate_utf8INS2_12utf8_checkerEEEbPKhm:
   46|  4.20k|bool generic_validate_utf8(const uint8_t *input, size_t length) {
   47|  4.20k|  checker c{};
   48|  4.20k|  buf_block_reader<64> reader(input, length);
   49|  3.30M|  while (reader.has_full_block()) {
  ------------------
  |  Branch (49:10): [True: 3.29M, False: 4.20k]
  ------------------
   50|  3.29M|    simd::simd8x64<uint8_t> in(reader.full_block());
   51|  3.29M|    c.check_next_input(in);
   52|  3.29M|    reader.advance();
   53|  3.29M|  }
   54|  4.20k|  check_remainder(c, input, length, reader.block_index());
   55|  4.20k|  return !c.errors();
   56|  4.20k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation15check_remainderINS2_12utf8_checkerEEEvRT_PKhmm:
   15|  7.41k|                                           size_t length, size_t idx) {
   16|  7.41k|  constexpr size_t block = sizeof(simd::simd8x64<uint8_t>);
   17|  7.41k|  constexpr size_t width = sizeof(simd::simd8<uint8_t>);
   18|  7.41k|  static_assert(block == 8 * sizeof(uint64_t), "one mask bit per byte");
   19|  7.41k|  if (length >= block + width) {
  ------------------
  |  Branch (19:7): [True: 3.13k, False: 4.28k]
  ------------------
   20|       |    // The window starts at or before idx: buf_block_reader leaves 1 to 64
   21|       |    // bytes, so 0 <= idx - start < 64.
   22|  3.13k|    const size_t start = length - block;
   23|  3.13k|    const simd::simd8x64<uint8_t> in(input + start);
   24|       |    // Bit i is set if byte start + i is not ASCII, so the bytes from idx on
   25|       |    // are the top length - idx bits. is_ascii() goes first because GCC, up
   26|       |    // to at least GCC 17, builds gteq_unsigned(0x80) from min and compare
   27|       |    // instead of one movemask, which costs ASCII tails about 0.5 ns.
   28|  3.13k|    if (!in.is_ascii() && (in.gteq_unsigned(0x80) >> (idx - start)) != 0) {
  ------------------
  |  Branch (28:9): [True: 2.22k, False: 913]
  |  Branch (28:27): [True: 1.79k, False: 433]
  ------------------
   29|  1.79k|      c.check_overlapping_input(in,
   30|  1.79k|                                simd::simd8<uint8_t>(input + start - width));
   31|  1.79k|    }
   32|  4.28k|  } else if (idx != length) {
  ------------------
  |  Branch (32:14): [True: 4.24k, False: 36]
  ------------------
   33|       |    // Only an empty input leaves no bytes. Its pointer may be null, and
   34|       |    // memcpy from a null pointer is undefined.
   35|  4.24k|    uint8_t buffer[block]{};
   36|  4.24k|    std::memcpy(buffer, input + idx, length - idx);
   37|  4.24k|    c.check_next_input(simd::simd8x64<uint8_t>(buffer));
   38|  4.24k|  }
   39|  7.41k|  c.check_eof();
   40|  7.41k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation33generic_validate_utf8_with_errorsEPKcm:
  104|  4.20k|result generic_validate_utf8_with_errors(const char *input, size_t length) {
  105|  4.20k|  return generic_validate_utf8_with_errors<utf8_checker>(
  106|  4.20k|      reinterpret_cast<const uint8_t *>(input), length);
  107|  4.20k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation33generic_validate_utf8_with_errorsINS2_12utf8_checkerEEENS_6resultEPKhm:
   67|  4.20k|result generic_validate_utf8_with_errors(const uint8_t *input, size_t length) {
   68|  4.20k|  checker c{};
   69|  4.20k|  buf_block_reader<64> reader(input, length);
   70|  4.20k|  size_t count{0};
   71|  2.24M|  while (reader.has_full_block()) {
  ------------------
  |  Branch (71:10): [True: 2.24M, False: 3.20k]
  ------------------
   72|  2.24M|    simd::simd8x64<uint8_t> in(reader.full_block());
   73|  2.24M|    c.check_next_input(in);
   74|  2.24M|    if (c.errors()) {
  ------------------
  |  Branch (74:9): [True: 1.00k, False: 2.24M]
  ------------------
   75|  1.00k|      if (count != 0) {
  ------------------
  |  Branch (75:11): [True: 285, False: 715]
  ------------------
   76|    285|        count--;
   77|    285|      } // Sometimes the error is only detected in the next chunk
   78|  1.00k|      SIMDUTF_VZEROUPPER();
  ------------------
  |  |   16|  1.00k|#define SIMDUTF_VZEROUPPER() (void)0
  ------------------
   79|  1.00k|      result res = scalar::utf8::rewind_and_validate_with_errors(
   80|  1.00k|          reinterpret_cast<const char *>(input),
   81|  1.00k|          reinterpret_cast<const char *>(input + count), length - count);
   82|  1.00k|      res.count += count;
   83|  1.00k|      return res;
   84|  1.00k|    }
   85|  2.24M|    reader.advance();
   86|  2.24M|    count += 64;
   87|  2.24M|  }
   88|  3.20k|  check_remainder(c, input, length, reader.block_index());
   89|  3.20k|  if (c.errors()) {
  ------------------
  |  Branch (89:7): [True: 1.42k, False: 1.78k]
  ------------------
   90|  1.42k|    if (count != 0) {
  ------------------
  |  Branch (90:9): [True: 510, False: 918]
  ------------------
   91|    510|      count--;
   92|    510|    } // Sometimes the error is only detected in the next chunk
   93|  1.42k|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   16|  1.42k|#define SIMDUTF_VZEROUPPER() (void)0
  ------------------
   94|  1.42k|    result res = scalar::utf8::rewind_and_validate_with_errors(
   95|  1.42k|        reinterpret_cast<const char *>(input),
   96|  1.42k|        reinterpret_cast<const char *>(input) + count, length - count);
   97|  1.42k|    res.count += count;
   98|  1.42k|    return res;
   99|  1.78k|  } else {
  100|  1.78k|    return result(error_code::SUCCESS, length);
  101|  1.78k|  }
  102|  3.20k|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1626validate_utf16_with_errorsILNS_10endiannessE0EEEKNS_6resultEPKDsm:
   49|  2.85k|const result validate_utf16_with_errors(const char16_t *input, size_t size) {
   50|  2.85k|  if (simdutf_unlikely(size == 0)) {
  ------------------
  |  |   93|  2.85k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (93:33): [True: 17, False: 2.83k]
  |  |  ------------------
  ------------------
   51|     17|    return result(error_code::SUCCESS, 0);
   52|     17|  }
   53|       |
   54|  2.83k|  const char16_t *start = input;
   55|  2.83k|  const char16_t *end = input + size;
   56|       |
   57|  2.83k|  const auto v_d8 = simd8<uint8_t>::splat(0xd8);
   58|  2.83k|  const auto v_f8 = simd8<uint8_t>::splat(0xf8);
   59|  2.83k|  const auto v_fc = simd8<uint8_t>::splat(0xfc);
   60|  2.83k|  const auto v_dc = simd8<uint8_t>::splat(0xdc);
   61|       |
   62|  2.72M|  while (input + simd16<uint16_t>::SIZE * 2 < end) {
  ------------------
  |  Branch (62:10): [True: 2.71M, False: 2.64k]
  ------------------
   63|       |    // 0. Load data: since the validation takes into account only higher
   64|       |    //    byte of each word, we compress the two vectors into one which
   65|       |    //    consists only the higher bytes.
   66|  2.71M|    auto in0 = simd16<uint16_t>(input);
   67|  2.71M|    auto in1 =
   68|  2.71M|        simd16<uint16_t>(input + simd16<uint16_t>::SIZE / sizeof(char16_t));
   69|       |
   70|       |    // Function `utf16_gather_high_bytes` consumes two vectors of UTF-16
   71|       |    // and yields a single vector having only higher bytes of characters.
   72|  2.71M|    const auto in = utf16_gather_high_bytes<big_endian>(in0, in1);
   73|       |
   74|       |    // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy).
   75|  2.71M|    const auto surrogates_wordmask = (in & v_f8) == v_d8;
   76|  2.71M|    const uint16_t surrogates_bitmask =
   77|  2.71M|        static_cast<uint16_t>(surrogates_wordmask.to_bitmask());
   78|  2.71M|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (78:9): [True: 2.69M, False: 26.4k]
  ------------------
   79|  2.69M|      input += 16;
   80|  2.69M|    } else {
   81|       |      // 2. We have some surrogates that have to be distinguished:
   82|       |      //    - low  surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF)
   83|       |      //    - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF)
   84|       |      //
   85|       |      //    Fact: high surrogate has 11th bit set (3rd bit in the higher byte)
   86|       |
   87|       |      // V - non-surrogate code units
   88|       |      //     V = not surrogates_wordmask
   89|  26.4k|      const uint16_t V = static_cast<uint16_t>(~surrogates_bitmask);
   90|       |
   91|       |      // H - word-mask for high surrogates: the six highest bits are 0b1101'11
   92|  26.4k|      const auto vH = (in & v_fc) == v_dc;
   93|  26.4k|      const uint16_t H = static_cast<uint16_t>(vH.to_bitmask());
   94|       |
   95|       |      // L - word mask for low surrogates
   96|       |      //     L = not H and surrogates_wordmask
   97|  26.4k|      const uint16_t L = static_cast<uint16_t>(~H & surrogates_bitmask);
   98|       |
   99|  26.4k|      const uint16_t a = static_cast<uint16_t>(
  100|  26.4k|          L & (H >> 1)); // A low surrogate must be followed by high one.
  101|       |                         // (A low surrogate placed in the 7th register's word
  102|       |                         // is an exception we handle.)
  103|  26.4k|      const uint16_t b = static_cast<uint16_t>(
  104|  26.4k|          a << 1); // Just mark that the opinput - startite fact is hold,
  105|       |                   // thanks to that we have only two masks for valid case.
  106|  26.4k|      const uint16_t c = static_cast<uint16_t>(
  107|  26.4k|          V | a | b); // Combine all the masks into the final one.
  108|       |
  109|  26.4k|      if (c == 0xffff) {
  ------------------
  |  Branch (109:11): [True: 19.9k, False: 6.50k]
  ------------------
  110|       |        // The whole input register contains valid UTF-16, i.e.,
  111|       |        // either single code units or proper surrogate pairs.
  112|  19.9k|        input += 16;
  113|  19.9k|      } else if (c == 0x7fff) {
  ------------------
  |  Branch (113:18): [True: 6.31k, False: 192]
  ------------------
  114|       |        // The 15 lower code units of the input register contains valid UTF-16.
  115|       |        // The 15th word may be either a low or high surrogate. It the next
  116|       |        // iteration we 1) check if the low surrogate is followed by a high
  117|       |        // one, 2) reject sole high surrogate.
  118|  6.31k|        input += 15;
  119|  6.31k|      } else {
  120|    192|        return result(error_code::SURROGATE, input - start);
  121|    192|      }
  122|  26.4k|    }
  123|  2.71M|  }
  124|       |
  125|  2.64k|  return result(error_code::SUCCESS, input - start);
  126|  2.83k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1626validate_utf16_with_errorsILNS_10endiannessE1EEEKNS_6resultEPKDsm:
   49|  2.62k|const result validate_utf16_with_errors(const char16_t *input, size_t size) {
   50|  2.62k|  if (simdutf_unlikely(size == 0)) {
  ------------------
  |  |   93|  2.62k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (93:33): [True: 16, False: 2.61k]
  |  |  ------------------
  ------------------
   51|     16|    return result(error_code::SUCCESS, 0);
   52|     16|  }
   53|       |
   54|  2.61k|  const char16_t *start = input;
   55|  2.61k|  const char16_t *end = input + size;
   56|       |
   57|  2.61k|  const auto v_d8 = simd8<uint8_t>::splat(0xd8);
   58|  2.61k|  const auto v_f8 = simd8<uint8_t>::splat(0xf8);
   59|  2.61k|  const auto v_fc = simd8<uint8_t>::splat(0xfc);
   60|  2.61k|  const auto v_dc = simd8<uint8_t>::splat(0xdc);
   61|       |
   62|  2.64M|  while (input + simd16<uint16_t>::SIZE * 2 < end) {
  ------------------
  |  Branch (62:10): [True: 2.63M, False: 2.41k]
  ------------------
   63|       |    // 0. Load data: since the validation takes into account only higher
   64|       |    //    byte of each word, we compress the two vectors into one which
   65|       |    //    consists only the higher bytes.
   66|  2.63M|    auto in0 = simd16<uint16_t>(input);
   67|  2.63M|    auto in1 =
   68|  2.63M|        simd16<uint16_t>(input + simd16<uint16_t>::SIZE / sizeof(char16_t));
   69|       |
   70|       |    // Function `utf16_gather_high_bytes` consumes two vectors of UTF-16
   71|       |    // and yields a single vector having only higher bytes of characters.
   72|  2.63M|    const auto in = utf16_gather_high_bytes<big_endian>(in0, in1);
   73|       |
   74|       |    // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy).
   75|  2.63M|    const auto surrogates_wordmask = (in & v_f8) == v_d8;
   76|  2.63M|    const uint16_t surrogates_bitmask =
   77|  2.63M|        static_cast<uint16_t>(surrogates_wordmask.to_bitmask());
   78|  2.63M|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (78:9): [True: 2.60M, False: 36.3k]
  ------------------
   79|  2.60M|      input += 16;
   80|  2.60M|    } else {
   81|       |      // 2. We have some surrogates that have to be distinguished:
   82|       |      //    - low  surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF)
   83|       |      //    - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF)
   84|       |      //
   85|       |      //    Fact: high surrogate has 11th bit set (3rd bit in the higher byte)
   86|       |
   87|       |      // V - non-surrogate code units
   88|       |      //     V = not surrogates_wordmask
   89|  36.3k|      const uint16_t V = static_cast<uint16_t>(~surrogates_bitmask);
   90|       |
   91|       |      // H - word-mask for high surrogates: the six highest bits are 0b1101'11
   92|  36.3k|      const auto vH = (in & v_fc) == v_dc;
   93|  36.3k|      const uint16_t H = static_cast<uint16_t>(vH.to_bitmask());
   94|       |
   95|       |      // L - word mask for low surrogates
   96|       |      //     L = not H and surrogates_wordmask
   97|  36.3k|      const uint16_t L = static_cast<uint16_t>(~H & surrogates_bitmask);
   98|       |
   99|  36.3k|      const uint16_t a = static_cast<uint16_t>(
  100|  36.3k|          L & (H >> 1)); // A low surrogate must be followed by high one.
  101|       |                         // (A low surrogate placed in the 7th register's word
  102|       |                         // is an exception we handle.)
  103|  36.3k|      const uint16_t b = static_cast<uint16_t>(
  104|  36.3k|          a << 1); // Just mark that the opinput - startite fact is hold,
  105|       |                   // thanks to that we have only two masks for valid case.
  106|  36.3k|      const uint16_t c = static_cast<uint16_t>(
  107|  36.3k|          V | a | b); // Combine all the masks into the final one.
  108|       |
  109|  36.3k|      if (c == 0xffff) {
  ------------------
  |  Branch (109:11): [True: 26.4k, False: 9.92k]
  ------------------
  110|       |        // The whole input register contains valid UTF-16, i.e.,
  111|       |        // either single code units or proper surrogate pairs.
  112|  26.4k|        input += 16;
  113|  26.4k|      } else if (c == 0x7fff) {
  ------------------
  |  Branch (113:18): [True: 9.72k, False: 196]
  ------------------
  114|       |        // The 15 lower code units of the input register contains valid UTF-16.
  115|       |        // The 15th word may be either a low or high surrogate. It the next
  116|       |        // iteration we 1) check if the low surrogate is followed by a high
  117|       |        // one, 2) reject sole high surrogate.
  118|  9.72k|        input += 15;
  119|  9.72k|      } else {
  120|    196|        return result(error_code::SURROGATE, input - start);
  121|    196|      }
  122|  36.3k|    }
  123|  2.63M|  }
  124|       |
  125|  2.41k|  return result(error_code::SUCCESS, input - start);
  126|  2.61k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1626validate_utf16_with_errorsILNS_10endiannessE0EEEKNS_6resultEPKDsm:
   49|  2.85k|const result validate_utf16_with_errors(const char16_t *input, size_t size) {
   50|  2.85k|  if (simdutf_unlikely(size == 0)) {
  ------------------
  |  |   93|  2.85k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (93:33): [True: 17, False: 2.83k]
  |  |  ------------------
  ------------------
   51|     17|    return result(error_code::SUCCESS, 0);
   52|     17|  }
   53|       |
   54|  2.83k|  const char16_t *start = input;
   55|  2.83k|  const char16_t *end = input + size;
   56|       |
   57|  2.83k|  const auto v_d8 = simd8<uint8_t>::splat(0xd8);
   58|  2.83k|  const auto v_f8 = simd8<uint8_t>::splat(0xf8);
   59|  2.83k|  const auto v_fc = simd8<uint8_t>::splat(0xfc);
   60|  2.83k|  const auto v_dc = simd8<uint8_t>::splat(0xdc);
   61|       |
   62|  2.72M|  while (input + simd16<uint16_t>::SIZE * 2 < end) {
  ------------------
  |  Branch (62:10): [True: 2.71M, False: 2.55k]
  ------------------
   63|       |    // 0. Load data: since the validation takes into account only higher
   64|       |    //    byte of each word, we compress the two vectors into one which
   65|       |    //    consists only the higher bytes.
   66|  2.71M|    auto in0 = simd16<uint16_t>(input);
   67|  2.71M|    auto in1 =
   68|  2.71M|        simd16<uint16_t>(input + simd16<uint16_t>::SIZE / sizeof(char16_t));
   69|       |
   70|       |    // Function `utf16_gather_high_bytes` consumes two vectors of UTF-16
   71|       |    // and yields a single vector having only higher bytes of characters.
   72|  2.71M|    const auto in = utf16_gather_high_bytes<big_endian>(in0, in1);
   73|       |
   74|       |    // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy).
   75|  2.71M|    const auto surrogates_wordmask = (in & v_f8) == v_d8;
   76|  2.71M|    const uint16_t surrogates_bitmask =
   77|  2.71M|        static_cast<uint16_t>(surrogates_wordmask.to_bitmask());
   78|  2.71M|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (78:9): [True: 2.69M, False: 26.9k]
  ------------------
   79|  2.69M|      input += 16;
   80|  2.69M|    } else {
   81|       |      // 2. We have some surrogates that have to be distinguished:
   82|       |      //    - low  surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF)
   83|       |      //    - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF)
   84|       |      //
   85|       |      //    Fact: high surrogate has 11th bit set (3rd bit in the higher byte)
   86|       |
   87|       |      // V - non-surrogate code units
   88|       |      //     V = not surrogates_wordmask
   89|  26.9k|      const uint16_t V = static_cast<uint16_t>(~surrogates_bitmask);
   90|       |
   91|       |      // H - word-mask for high surrogates: the six highest bits are 0b1101'11
   92|  26.9k|      const auto vH = (in & v_fc) == v_dc;
   93|  26.9k|      const uint16_t H = static_cast<uint16_t>(vH.to_bitmask());
   94|       |
   95|       |      // L - word mask for low surrogates
   96|       |      //     L = not H and surrogates_wordmask
   97|  26.9k|      const uint16_t L = static_cast<uint16_t>(~H & surrogates_bitmask);
   98|       |
   99|  26.9k|      const uint16_t a = static_cast<uint16_t>(
  100|  26.9k|          L & (H >> 1)); // A low surrogate must be followed by high one.
  101|       |                         // (A low surrogate placed in the 7th register's word
  102|       |                         // is an exception we handle.)
  103|  26.9k|      const uint16_t b = static_cast<uint16_t>(
  104|  26.9k|          a << 1); // Just mark that the opinput - startite fact is hold,
  105|       |                   // thanks to that we have only two masks for valid case.
  106|  26.9k|      const uint16_t c = static_cast<uint16_t>(
  107|  26.9k|          V | a | b); // Combine all the masks into the final one.
  108|       |
  109|  26.9k|      if (c == 0xffff) {
  ------------------
  |  Branch (109:11): [True: 20.2k, False: 6.67k]
  ------------------
  110|       |        // The whole input register contains valid UTF-16, i.e.,
  111|       |        // either single code units or proper surrogate pairs.
  112|  20.2k|        input += 16;
  113|  20.2k|      } else if (c == 0x7fff) {
  ------------------
  |  Branch (113:18): [True: 6.38k, False: 284]
  ------------------
  114|       |        // The 15 lower code units of the input register contains valid UTF-16.
  115|       |        // The 15th word may be either a low or high surrogate. It the next
  116|       |        // iteration we 1) check if the low surrogate is followed by a high
  117|       |        // one, 2) reject sole high surrogate.
  118|  6.38k|        input += 15;
  119|  6.38k|      } else {
  120|    284|        return result(error_code::SURROGATE, input - start);
  121|    284|      }
  122|  26.9k|    }
  123|  2.71M|  }
  124|       |
  125|  2.55k|  return result(error_code::SUCCESS, input - start);
  126|  2.83k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1626validate_utf16_with_errorsILNS_10endiannessE1EEEKNS_6resultEPKDsm:
   49|  2.62k|const result validate_utf16_with_errors(const char16_t *input, size_t size) {
   50|  2.62k|  if (simdutf_unlikely(size == 0)) {
  ------------------
  |  |   93|  2.62k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (93:33): [True: 16, False: 2.61k]
  |  |  ------------------
  ------------------
   51|     16|    return result(error_code::SUCCESS, 0);
   52|     16|  }
   53|       |
   54|  2.61k|  const char16_t *start = input;
   55|  2.61k|  const char16_t *end = input + size;
   56|       |
   57|  2.61k|  const auto v_d8 = simd8<uint8_t>::splat(0xd8);
   58|  2.61k|  const auto v_f8 = simd8<uint8_t>::splat(0xf8);
   59|  2.61k|  const auto v_fc = simd8<uint8_t>::splat(0xfc);
   60|  2.61k|  const auto v_dc = simd8<uint8_t>::splat(0xdc);
   61|       |
   62|  2.64M|  while (input + simd16<uint16_t>::SIZE * 2 < end) {
  ------------------
  |  Branch (62:10): [True: 2.63M, False: 2.35k]
  ------------------
   63|       |    // 0. Load data: since the validation takes into account only higher
   64|       |    //    byte of each word, we compress the two vectors into one which
   65|       |    //    consists only the higher bytes.
   66|  2.63M|    auto in0 = simd16<uint16_t>(input);
   67|  2.63M|    auto in1 =
   68|  2.63M|        simd16<uint16_t>(input + simd16<uint16_t>::SIZE / sizeof(char16_t));
   69|       |
   70|       |    // Function `utf16_gather_high_bytes` consumes two vectors of UTF-16
   71|       |    // and yields a single vector having only higher bytes of characters.
   72|  2.63M|    const auto in = utf16_gather_high_bytes<big_endian>(in0, in1);
   73|       |
   74|       |    // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy).
   75|  2.63M|    const auto surrogates_wordmask = (in & v_f8) == v_d8;
   76|  2.63M|    const uint16_t surrogates_bitmask =
   77|  2.63M|        static_cast<uint16_t>(surrogates_wordmask.to_bitmask());
   78|  2.63M|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (78:9): [True: 2.60M, False: 36.8k]
  ------------------
   79|  2.60M|      input += 16;
   80|  2.60M|    } else {
   81|       |      // 2. We have some surrogates that have to be distinguished:
   82|       |      //    - low  surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF)
   83|       |      //    - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF)
   84|       |      //
   85|       |      //    Fact: high surrogate has 11th bit set (3rd bit in the higher byte)
   86|       |
   87|       |      // V - non-surrogate code units
   88|       |      //     V = not surrogates_wordmask
   89|  36.8k|      const uint16_t V = static_cast<uint16_t>(~surrogates_bitmask);
   90|       |
   91|       |      // H - word-mask for high surrogates: the six highest bits are 0b1101'11
   92|  36.8k|      const auto vH = (in & v_fc) == v_dc;
   93|  36.8k|      const uint16_t H = static_cast<uint16_t>(vH.to_bitmask());
   94|       |
   95|       |      // L - word mask for low surrogates
   96|       |      //     L = not H and surrogates_wordmask
   97|  36.8k|      const uint16_t L = static_cast<uint16_t>(~H & surrogates_bitmask);
   98|       |
   99|  36.8k|      const uint16_t a = static_cast<uint16_t>(
  100|  36.8k|          L & (H >> 1)); // A low surrogate must be followed by high one.
  101|       |                         // (A low surrogate placed in the 7th register's word
  102|       |                         // is an exception we handle.)
  103|  36.8k|      const uint16_t b = static_cast<uint16_t>(
  104|  36.8k|          a << 1); // Just mark that the opinput - startite fact is hold,
  105|       |                   // thanks to that we have only two masks for valid case.
  106|  36.8k|      const uint16_t c = static_cast<uint16_t>(
  107|  36.8k|          V | a | b); // Combine all the masks into the final one.
  108|       |
  109|  36.8k|      if (c == 0xffff) {
  ------------------
  |  Branch (109:11): [True: 26.7k, False: 10.1k]
  ------------------
  110|       |        // The whole input register contains valid UTF-16, i.e.,
  111|       |        // either single code units or proper surrogate pairs.
  112|  26.7k|        input += 16;
  113|  26.7k|      } else if (c == 0x7fff) {
  ------------------
  |  Branch (113:18): [True: 9.84k, False: 254]
  ------------------
  114|       |        // The 15 lower code units of the input register contains valid UTF-16.
  115|       |        // The 15th word may be either a low or high surrogate. It the next
  116|       |        // iteration we 1) check if the low surrogate is followed by a high
  117|       |        // one, 2) reject sole high surrogate.
  118|  9.84k|        input += 15;
  119|  9.84k|      } else {
  120|    254|        return result(error_code::SURROGATE, input - start);
  121|    254|      }
  122|  36.8k|    }
  123|  2.63M|  }
  124|       |
  125|  2.35k|  return result(error_code::SUCCESS, input - start);
  126|  2.61k|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf328validateEPKDim:
    6|  2.64k|simdutf_really_inline bool validate(const char32_t *input, size_t size) {
    7|  2.64k|  if (simdutf_unlikely(size == 0)) {
  ------------------
  |  |   93|  2.64k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (93:33): [True: 23, False: 2.62k]
  |  |  ------------------
  ------------------
    8|       |    // empty input is valid UTF-32. protect the implementation from
    9|       |    // handling nullptr
   10|     23|    return true;
   11|     23|  }
   12|       |
   13|  2.62k|  const char32_t *end = input + size;
   14|       |
   15|  2.62k|  using vector_u32 = simd32<uint32_t>;
   16|       |
   17|  2.62k|  const auto standardmax = vector_u32::splat(0x10ffff);
   18|  2.62k|  const auto offset = vector_u32::splat(0xffff2000);
   19|  2.62k|  const auto standardoffsetmax = vector_u32::splat(0xfffff7ff);
   20|  2.62k|  auto currentmax = vector_u32::zero();
   21|  2.62k|  auto currentoffsetmax = vector_u32::zero();
   22|       |
   23|  2.62k|  constexpr size_t N = vector_u32::ELEMENTS;
   24|       |
   25|  1.21M|  while (input + N < end) {
  ------------------
  |  Branch (25:10): [True: 1.21M, False: 2.62k]
  ------------------
   26|  1.21M|    auto in = vector_u32(input);
   27|  1.21M|    if constexpr (!match_system(endianness::BIG)) {
   28|  1.21M|      in.swap_bytes();
   29|  1.21M|    }
   30|       |
   31|  1.21M|    currentmax = max(currentmax, in);
   32|  1.21M|    currentoffsetmax = max(currentoffsetmax, in + offset);
   33|  1.21M|    input += N;
   34|  1.21M|  }
   35|       |
   36|  2.62k|  const auto too_large = currentmax > standardmax;
   37|  2.62k|  if (too_large.any()) {
  ------------------
  |  Branch (37:7): [True: 956, False: 1.66k]
  ------------------
   38|    956|    return false;
   39|    956|  }
   40|       |
   41|  1.66k|  const auto surrogate = currentoffsetmax > standardoffsetmax;
   42|  1.66k|  if (surrogate.any()) {
  ------------------
  |  Branch (42:7): [True: 26, False: 1.64k]
  ------------------
   43|     26|    return false;
   44|     26|  }
   45|       |
   46|  1.64k|  return scalar::utf32::validate(input, end - input);
   47|  1.66k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf3220validate_with_errorsEPKDim:
   50|  2.64k|                                                  size_t size) {
   51|  2.64k|  if (simdutf_unlikely(size == 0)) {
  ------------------
  |  |   93|  2.64k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (93:33): [True: 23, False: 2.62k]
  |  |  ------------------
  ------------------
   52|       |    // empty input is valid UTF-32. protect the implementation from
   53|       |    // handling nullptr
   54|     23|    return result(error_code::SUCCESS, 0);
   55|     23|  }
   56|       |
   57|  2.62k|  const char32_t *start = input;
   58|  2.62k|  const char32_t *end = input + size;
   59|       |
   60|  2.62k|  using vector_u32 = simd32<uint32_t>;
   61|       |
   62|  2.62k|  const auto standardmax = vector_u32::splat(0x10ffff + 1);
   63|  2.62k|  const auto surrogate_mask = vector_u32::splat(0xfffff800);
   64|  2.62k|  const auto surrogate_byte = vector_u32::splat(0x0000d800);
   65|       |
   66|  2.62k|  constexpr size_t N = vector_u32::ELEMENTS;
   67|       |
   68|   147k|  while (input + N < end) {
  ------------------
  |  Branch (68:10): [True: 145k, False: 1.64k]
  ------------------
   69|   145k|    auto in = vector_u32(input);
   70|   145k|    if constexpr (!match_system(endianness::BIG)) {
   71|   145k|      in.swap_bytes();
   72|   145k|    }
   73|       |
   74|   145k|    const auto too_large = in >= standardmax;
   75|   145k|    const auto surrogate = (in & surrogate_mask) == surrogate_byte;
   76|       |
   77|   145k|    const auto combined = too_large | surrogate;
   78|   145k|    if (simdutf_unlikely(combined.any())) {
  ------------------
  |  |   93|   145k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (93:33): [True: 982, False: 144k]
  |  |  ------------------
  ------------------
   79|    982|      const size_t consumed = input - start;
   80|    982|      auto sr = scalar::utf32::validate_with_errors(input, end - input);
   81|    982|      sr.count += consumed;
   82|       |
   83|    982|      return sr;
   84|    982|    }
   85|       |
   86|   144k|    input += N;
   87|   144k|  }
   88|       |
   89|  1.64k|  const size_t consumed = input - start;
   90|  1.64k|  auto sr = scalar::utf32::validate_with_errors(input, end - input);
   91|  1.64k|  sr.count += consumed;
   92|       |
   93|  1.64k|  return sr;
   94|  2.62k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf328validateEPKDim:
    6|  2.64k|simdutf_really_inline bool validate(const char32_t *input, size_t size) {
    7|  2.64k|  if (simdutf_unlikely(size == 0)) {
  ------------------
  |  |   93|  2.64k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (93:33): [True: 23, False: 2.62k]
  |  |  ------------------
  ------------------
    8|       |    // empty input is valid UTF-32. protect the implementation from
    9|       |    // handling nullptr
   10|     23|    return true;
   11|     23|  }
   12|       |
   13|  2.62k|  const char32_t *end = input + size;
   14|       |
   15|  2.62k|  using vector_u32 = simd32<uint32_t>;
   16|       |
   17|  2.62k|  const auto standardmax = vector_u32::splat(0x10ffff);
   18|  2.62k|  const auto offset = vector_u32::splat(0xffff2000);
   19|  2.62k|  const auto standardoffsetmax = vector_u32::splat(0xfffff7ff);
   20|  2.62k|  auto currentmax = vector_u32::zero();
   21|  2.62k|  auto currentoffsetmax = vector_u32::zero();
   22|       |
   23|  2.62k|  constexpr size_t N = vector_u32::ELEMENTS;
   24|       |
   25|  2.42M|  while (input + N < end) {
  ------------------
  |  Branch (25:10): [True: 2.42M, False: 2.62k]
  ------------------
   26|  2.42M|    auto in = vector_u32(input);
   27|  2.42M|    if constexpr (!match_system(endianness::BIG)) {
   28|  2.42M|      in.swap_bytes();
   29|  2.42M|    }
   30|       |
   31|  2.42M|    currentmax = max(currentmax, in);
   32|  2.42M|    currentoffsetmax = max(currentoffsetmax, in + offset);
   33|  2.42M|    input += N;
   34|  2.42M|  }
   35|       |
   36|  2.62k|  const auto too_large = currentmax > standardmax;
   37|  2.62k|  if (too_large.any()) {
  ------------------
  |  Branch (37:7): [True: 1.12k, False: 1.50k]
  ------------------
   38|  1.12k|    return false;
   39|  1.12k|  }
   40|       |
   41|  1.50k|  const auto surrogate = currentoffsetmax > standardoffsetmax;
   42|  1.50k|  if (surrogate.any()) {
  ------------------
  |  Branch (42:7): [True: 44, False: 1.45k]
  ------------------
   43|     44|    return false;
   44|     44|  }
   45|       |
   46|  1.45k|  return scalar::utf32::validate(input, end - input);
   47|  1.50k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf3220validate_with_errorsEPKDim:
   50|  2.64k|                                                  size_t size) {
   51|  2.64k|  if (simdutf_unlikely(size == 0)) {
  ------------------
  |  |   93|  2.64k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (93:33): [True: 23, False: 2.62k]
  |  |  ------------------
  ------------------
   52|       |    // empty input is valid UTF-32. protect the implementation from
   53|       |    // handling nullptr
   54|     23|    return result(error_code::SUCCESS, 0);
   55|     23|  }
   56|       |
   57|  2.62k|  const char32_t *start = input;
   58|  2.62k|  const char32_t *end = input + size;
   59|       |
   60|  2.62k|  using vector_u32 = simd32<uint32_t>;
   61|       |
   62|  2.62k|  const auto standardmax = vector_u32::splat(0x10ffff + 1);
   63|  2.62k|  const auto surrogate_mask = vector_u32::splat(0xfffff800);
   64|  2.62k|  const auto surrogate_byte = vector_u32::splat(0x0000d800);
   65|       |
   66|  2.62k|  constexpr size_t N = vector_u32::ELEMENTS;
   67|       |
   68|   291k|  while (input + N < end) {
  ------------------
  |  Branch (68:10): [True: 290k, False: 1.45k]
  ------------------
   69|   290k|    auto in = vector_u32(input);
   70|   290k|    if constexpr (!match_system(endianness::BIG)) {
   71|   290k|      in.swap_bytes();
   72|   290k|    }
   73|       |
   74|   290k|    const auto too_large = in >= standardmax;
   75|   290k|    const auto surrogate = (in & surrogate_mask) == surrogate_byte;
   76|       |
   77|   290k|    const auto combined = too_large | surrogate;
   78|   290k|    if (simdutf_unlikely(combined.any())) {
  ------------------
  |  |   93|   290k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (93:33): [True: 1.16k, False: 289k]
  |  |  ------------------
  ------------------
   79|  1.16k|      const size_t consumed = input - start;
   80|  1.16k|      auto sr = scalar::utf32::validate_with_errors(input, end - input);
   81|  1.16k|      sr.count += consumed;
   82|       |
   83|  1.16k|      return sr;
   84|  1.16k|    }
   85|       |
   86|   289k|    input += N;
   87|   289k|  }
   88|       |
   89|  1.45k|  const size_t consumed = input - start;
   90|  1.45k|  auto sr = scalar::utf32::validate_with_errors(input, end - input);
   91|  1.45k|  sr.count += consumed;
   92|       |
   93|  1.45k|  return sr;
   94|  2.62k|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_128avx2_convert_latin1_to_utf16ILNS_10endiannessE0EEENSt3__14pairIPKcPDsEES7_mS8_:
    4|     76|                             char16_t *utf16_output) {
    5|     76|  size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16
    6|       |
    7|     76|  size_t i = 0;
    8|   129k|  for (; i < rounded_len; i += 16) {
  ------------------
  |  Branch (8:10): [True: 129k, False: 76]
  ------------------
    9|       |    // Load 16 bytes from the address (input + i) into a xmm register
   10|   129k|    const __m128i latin1 =
   11|   129k|        _mm_loadu_si128(reinterpret_cast<const __m128i *>(latin1_input + i));
   12|       |
   13|       |    // Zero extend each byte in `in` to word
   14|   129k|    __m256i utf16 = _mm256_cvtepu8_epi16(latin1);
   15|       |
   16|   129k|    if (big_endian) {
  ------------------
  |  Branch (16:9): [Folded, False: 129k]
  ------------------
   17|      0|      const __m128i swap128 =
   18|      0|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   19|      0|      const __m256i swap = _mm256_set_m128i(swap128, swap128);
  ------------------
  |  |    4|      0|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    5|      0|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
   20|      0|      utf16 = _mm256_shuffle_epi8(utf16, swap);
   21|      0|    }
   22|       |
   23|       |    // Store the contents of xmm1 into the address pointed by (output + i)
   24|   129k|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf16_output + i), utf16);
   25|   129k|  }
   26|       |
   27|     76|  return std::make_pair(latin1_input + rounded_len, utf16_output + rounded_len);
   28|     76|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_128avx2_convert_latin1_to_utf16ILNS_10endiannessE1EEENSt3__14pairIPKcPDsEES7_mS8_:
    4|    110|                             char16_t *utf16_output) {
    5|    110|  size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16
    6|       |
    7|    110|  size_t i = 0;
    8|   143k|  for (; i < rounded_len; i += 16) {
  ------------------
  |  Branch (8:10): [True: 143k, False: 110]
  ------------------
    9|       |    // Load 16 bytes from the address (input + i) into a xmm register
   10|   143k|    const __m128i latin1 =
   11|   143k|        _mm_loadu_si128(reinterpret_cast<const __m128i *>(latin1_input + i));
   12|       |
   13|       |    // Zero extend each byte in `in` to word
   14|   143k|    __m256i utf16 = _mm256_cvtepu8_epi16(latin1);
   15|       |
   16|   143k|    if (big_endian) {
  ------------------
  |  Branch (16:9): [True: 143k, Folded]
  ------------------
   17|   143k|      const __m128i swap128 =
   18|   143k|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   19|   143k|      const __m256i swap = _mm256_set_m128i(swap128, swap128);
  ------------------
  |  |    4|   143k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    5|   143k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
   20|   143k|      utf16 = _mm256_shuffle_epi8(utf16, swap);
   21|   143k|    }
   22|       |
   23|       |    // Store the contents of xmm1 into the address pointed by (output + i)
   24|   143k|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf16_output + i), utf16);
   25|   143k|  }
   26|       |
   27|    110|  return std::make_pair(latin1_input + rounded_len, utf16_output + rounded_len);
   28|    110|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_128avx2_convert_latin1_to_utf32EPKcmPDi:
    3|     96|                             char32_t *utf32_output) {
    4|     96|  size_t rounded_len = ((len | 7) ^ 7); // Round down to nearest multiple of 8
    5|       |
    6|   637k|  for (size_t i = 0; i < rounded_len; i += 8) {
  ------------------
  |  Branch (6:22): [True: 637k, False: 96]
  ------------------
    7|       |    // Load 8 Latin1 characters into a 64-bit register
    8|   637k|    __m128i in = _mm_loadl_epi64((__m128i *)&buf[i]);
    9|       |
   10|       |    // Zero extend each set of 8 Latin1 characters to 8 32-bit integers using
   11|       |    // vpmovzxbd
   12|   637k|    __m256i out = _mm256_cvtepu8_epi32(in);
   13|       |
   14|       |    // Store the results back to memory
   15|   637k|    _mm256_storeu_si256((__m256i *)&utf32_output[i], out);
   16|   637k|  }
   17|       |
   18|       |  // return pointers pointing to where we left off
   19|     96|  return std::make_pair(buf + rounded_len, utf32_output + rounded_len);
   20|     96|}

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

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_128avx2_convert_utf16_to_latin1ILNS_10endiannessE0EEENSt3__14pairIPKDsPcEES7_mS8_:
    4|    120|                             char *latin1_output) {
    5|    120|  const char16_t *end = buf + len;
    6|  38.2k|  while (end - buf >= 32) {
  ------------------
  |  Branch (6:10): [True: 38.1k, False: 94]
  ------------------
    7|       |    // Load 16 UTF-16 characters into 256-bit AVX2 register
    8|  38.1k|    __m256i in0 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(buf));
    9|  38.1k|    __m256i in1 =
   10|  38.1k|        _mm256_loadu_si256(reinterpret_cast<const __m256i *>(buf + 16));
   11|       |
   12|       |    if constexpr (!match_system(big_endian)) {
   13|       |      const __m256i swap = _mm256_setr_epi8(
   14|       |          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   15|       |          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   16|       |      in0 = _mm256_shuffle_epi8(in0, swap);
   17|       |      in1 = _mm256_shuffle_epi8(in1, swap);
   18|       |    }
   19|       |
   20|  38.1k|    __m256i high_byte_mask = _mm256_set1_epi16((int16_t)0xFF00);
   21|  38.1k|    if (_mm256_testz_si256(_mm256_or_si256(in0, in1), high_byte_mask)) {
  ------------------
  |  Branch (21:9): [True: 38.0k, False: 26]
  ------------------
   22|       |      // Pack 16-bit characters into 8-bit and store in latin1_output
   23|  38.0k|      const __m256i packed = _mm256_packus_epi16(in0, in1);
   24|       |
   25|  38.0k|      const __m256i result = _mm256_permute4x64_epi64(packed, 0b11011000);
   26|       |
   27|  38.0k|      _mm256_storeu_si256(reinterpret_cast<__m256i *>(latin1_output), result);
   28|       |      // Adjust pointers for the next iteration
   29|  38.0k|      buf += 32;
   30|  38.0k|      latin1_output += 32;
   31|  38.0k|    } else {
   32|     26|      return std::make_pair(nullptr, reinterpret_cast<char *>(latin1_output));
   33|     26|    }
   34|  38.1k|  } // while
   35|     94|  return std::make_pair(buf, latin1_output);
   36|    120|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_128avx2_convert_utf16_to_latin1ILNS_10endiannessE1EEENSt3__14pairIPKDsPcEES7_mS8_:
    4|     98|                             char *latin1_output) {
    5|     98|  const char16_t *end = buf + len;
    6|    288|  while (end - buf >= 32) {
  ------------------
  |  Branch (6:10): [True: 196, False: 92]
  ------------------
    7|       |    // Load 16 UTF-16 characters into 256-bit AVX2 register
    8|    196|    __m256i in0 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(buf));
    9|    196|    __m256i in1 =
   10|    196|        _mm256_loadu_si256(reinterpret_cast<const __m256i *>(buf + 16));
   11|       |
   12|    196|    if constexpr (!match_system(big_endian)) {
   13|    196|      const __m256i swap = _mm256_setr_epi8(
   14|    196|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   15|    196|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   16|    196|      in0 = _mm256_shuffle_epi8(in0, swap);
   17|    196|      in1 = _mm256_shuffle_epi8(in1, swap);
   18|    196|    }
   19|       |
   20|    196|    __m256i high_byte_mask = _mm256_set1_epi16((int16_t)0xFF00);
   21|    196|    if (_mm256_testz_si256(_mm256_or_si256(in0, in1), high_byte_mask)) {
  ------------------
  |  Branch (21:9): [True: 190, False: 6]
  ------------------
   22|       |      // Pack 16-bit characters into 8-bit and store in latin1_output
   23|    190|      const __m256i packed = _mm256_packus_epi16(in0, in1);
   24|       |
   25|    190|      const __m256i result = _mm256_permute4x64_epi64(packed, 0b11011000);
   26|       |
   27|    190|      _mm256_storeu_si256(reinterpret_cast<__m256i *>(latin1_output), result);
   28|       |      // Adjust pointers for the next iteration
   29|    190|      buf += 32;
   30|    190|      latin1_output += 32;
   31|    190|    } else {
   32|      6|      return std::make_pair(nullptr, reinterpret_cast<char *>(latin1_output));
   33|      6|    }
   34|    196|  } // while
   35|     92|  return std::make_pair(buf, latin1_output);
   36|     98|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_140avx2_convert_utf16_to_latin1_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
   41|    280|                                         char *latin1_output) {
   42|    280|  const char16_t *start = buf;
   43|    280|  const char16_t *end = buf + len;
   44|   418k|  while (end - buf >= 16) {
  ------------------
  |  Branch (44:10): [True: 418k, False: 170]
  ------------------
   45|   418k|    __m256i in = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(buf));
   46|       |
   47|       |    if constexpr (!match_system(big_endian)) {
   48|       |      const __m256i swap = _mm256_setr_epi8(
   49|       |          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   50|       |          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   51|       |      in = _mm256_shuffle_epi8(in, swap);
   52|       |    }
   53|       |
   54|   418k|    __m256i high_byte_mask = _mm256_set1_epi16((int16_t)0xFF00);
   55|   418k|    if (_mm256_testz_si256(in, high_byte_mask)) {
  ------------------
  |  Branch (55:9): [True: 418k, False: 110]
  ------------------
   56|   418k|      __m128i lo = _mm256_extractf128_si256(in, 0);
   57|   418k|      __m128i hi = _mm256_extractf128_si256(in, 1);
   58|   418k|      __m128i latin1_packed_lo = _mm_packus_epi16(lo, lo);
   59|   418k|      __m128i latin1_packed_hi = _mm_packus_epi16(hi, hi);
   60|   418k|      _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output),
   61|   418k|                       latin1_packed_lo);
   62|   418k|      _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output + 8),
   63|   418k|                       latin1_packed_hi);
   64|   418k|      buf += 16;
   65|   418k|      latin1_output += 16;
   66|   418k|    } else {
   67|       |      // Fallback to scalar code for handling errors
   68|    536|      for (int k = 0; k < 16; k++) {
  ------------------
  |  Branch (68:23): [True: 536, False: 0]
  ------------------
   69|    536|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
   70|    536|        if (word <= 0xff) {
  ------------------
  |  Branch (70:13): [True: 426, False: 110]
  ------------------
   71|    426|          *latin1_output++ = char(word);
   72|    426|        } else {
   73|    110|          return std::make_pair(
   74|    110|              result{error_code::TOO_LARGE, (size_t)(buf - start + k)},
   75|    110|              latin1_output);
   76|    110|        }
   77|    536|      }
   78|      0|      buf += 16;
   79|      0|    }
   80|   418k|  } // while
   81|    170|  return std::make_pair(result{error_code::SUCCESS, (size_t)(buf - start)},
   82|    170|                        latin1_output);
   83|    280|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_140avx2_convert_utf16_to_latin1_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
   41|    248|                                         char *latin1_output) {
   42|    248|  const char16_t *start = buf;
   43|    248|  const char16_t *end = buf + len;
   44|   457k|  while (end - buf >= 16) {
  ------------------
  |  Branch (44:10): [True: 457k, False: 146]
  ------------------
   45|   457k|    __m256i in = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(buf));
   46|       |
   47|   457k|    if constexpr (!match_system(big_endian)) {
   48|   457k|      const __m256i swap = _mm256_setr_epi8(
   49|   457k|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   50|   457k|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   51|   457k|      in = _mm256_shuffle_epi8(in, swap);
   52|   457k|    }
   53|       |
   54|   457k|    __m256i high_byte_mask = _mm256_set1_epi16((int16_t)0xFF00);
   55|   457k|    if (_mm256_testz_si256(in, high_byte_mask)) {
  ------------------
  |  Branch (55:9): [True: 456k, False: 102]
  ------------------
   56|   456k|      __m128i lo = _mm256_extractf128_si256(in, 0);
   57|   456k|      __m128i hi = _mm256_extractf128_si256(in, 1);
   58|   456k|      __m128i latin1_packed_lo = _mm_packus_epi16(lo, lo);
   59|   456k|      __m128i latin1_packed_hi = _mm_packus_epi16(hi, hi);
   60|   456k|      _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output),
   61|   456k|                       latin1_packed_lo);
   62|   456k|      _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output + 8),
   63|   456k|                       latin1_packed_hi);
   64|   456k|      buf += 16;
   65|   456k|      latin1_output += 16;
   66|   456k|    } else {
   67|       |      // Fallback to scalar code for handling errors
   68|    496|      for (int k = 0; k < 16; k++) {
  ------------------
  |  Branch (68:23): [True: 496, False: 0]
  ------------------
   69|    496|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
   70|    496|        if (word <= 0xff) {
  ------------------
  |  Branch (70:13): [True: 394, False: 102]
  ------------------
   71|    394|          *latin1_output++ = char(word);
   72|    394|        } else {
   73|    102|          return std::make_pair(
   74|    102|              result{error_code::TOO_LARGE, (size_t)(buf - start + k)},
   75|    102|              latin1_output);
   76|    102|        }
   77|    496|      }
   78|      0|      buf += 16;
   79|      0|    }
   80|   457k|  } // while
   81|    146|  return std::make_pair(result{error_code::SUCCESS, (size_t)(buf - start)},
   82|    146|                        latin1_output);
   83|    248|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_127avx2_convert_utf16_to_utf32ILNS_10endiannessE0EEENSt3__14pairIPKDsPDiEES7_mS8_:
   57|    430|                            char32_t *utf32_output) {
   58|    430|  const char16_t *end = buf + len;
   59|    430|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
   60|    430|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
   61|       |
   62|   199k|  while (end - buf >= 16) {
  ------------------
  |  Branch (62:10): [True: 199k, False: 336]
  ------------------
   63|   199k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
   64|   199k|    if (big_endian) {
  ------------------
  |  Branch (64:9): [Folded, False: 199k]
  ------------------
   65|      0|      const __m256i swap = _mm256_setr_epi8(
   66|      0|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   67|      0|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   68|      0|      in = _mm256_shuffle_epi8(in, swap);
   69|      0|    }
   70|       |
   71|       |    // 1. Check if there are any surrogate word in the input chunk.
   72|       |    //    We have also deal with situation when there is a surrogate word
   73|       |    //    at the end of a chunk.
   74|   199k|    const __m256i surrogates_bytemask =
   75|   199k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800);
   76|       |
   77|       |    // bitmask = 0x0000 if there are no surrogates
   78|       |    //         = 0xc000 if the last word is a surrogate
   79|   199k|    const uint32_t surrogates_bitmask =
   80|   199k|        static_cast<uint32_t>(_mm256_movemask_epi8(surrogates_bytemask));
   81|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
   82|       |    // However, it is likely an uncommon occurrence.
   83|   199k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (83:9): [True: 192k, False: 6.43k]
  ------------------
   84|       |      // case: we extend all sixteen 16-bit code units to sixteen 32-bit code
   85|       |      // units
   86|   192k|      _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output),
   87|   192k|                          _mm256_cvtepu16_epi32(_mm256_castsi256_si128(in)));
   88|   192k|      _mm256_storeu_si256(
   89|   192k|          reinterpret_cast<__m256i *>(utf32_output + 8),
   90|   192k|          _mm256_cvtepu16_epi32(_mm256_extractf128_si256(in, 1)));
   91|   192k|      utf32_output += 16;
   92|   192k|      buf += 16;
   93|       |      // surrogate pair(s) in a register
   94|   192k|    } else {
   95|       |      // Let us do a scalar fallback.
   96|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
   97|       |      // in the presence of surrogate pairs may require non-trivial tables.
   98|  6.43k|      size_t forward = 15;
   99|  6.43k|      size_t k = 0;
  100|  6.43k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (100:11): [True: 0, False: 6.43k]
  ------------------
  101|      0|        forward = size_t(end - buf - 1);
  102|      0|      }
  103|  94.5k|      for (; k < forward; k++) {
  ------------------
  |  Branch (103:14): [True: 88.2k, False: 6.33k]
  ------------------
  104|  88.2k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  105|  88.2k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (105:13): [True: 80.5k, False: 7.71k]
  ------------------
  106|       |          // No surrogate pair
  107|  80.5k|          *utf32_output++ = char32_t(word);
  108|  80.5k|        } else {
  109|       |          // must be a surrogate pair
  110|  7.71k|          uint16_t diff = uint16_t(word - 0xD800);
  111|  7.71k|          uint16_t next_word =
  112|  7.71k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  113|  7.71k|          k++;
  114|  7.71k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  115|  7.71k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (115:15): [True: 94, False: 7.62k]
  ------------------
  116|     94|            return std::make_pair(nullptr, utf32_output);
  117|     94|          }
  118|  7.62k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  119|  7.62k|          *utf32_output++ = char32_t(value);
  120|  7.62k|        }
  121|  88.2k|      }
  122|  6.33k|      buf += k;
  123|  6.33k|    }
  124|   199k|  } // while
  125|    336|  return std::make_pair(buf, utf32_output);
  126|    430|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_127avx2_convert_utf16_to_utf32ILNS_10endiannessE1EEENSt3__14pairIPKDsPDiEES7_mS8_:
   57|    458|                            char32_t *utf32_output) {
   58|    458|  const char16_t *end = buf + len;
   59|    458|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
   60|    458|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
   61|       |
   62|   477k|  while (end - buf >= 16) {
  ------------------
  |  Branch (62:10): [True: 477k, False: 376]
  ------------------
   63|   477k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
   64|   477k|    if (big_endian) {
  ------------------
  |  Branch (64:9): [True: 477k, Folded]
  ------------------
   65|   477k|      const __m256i swap = _mm256_setr_epi8(
   66|   477k|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   67|   477k|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   68|   477k|      in = _mm256_shuffle_epi8(in, swap);
   69|   477k|    }
   70|       |
   71|       |    // 1. Check if there are any surrogate word in the input chunk.
   72|       |    //    We have also deal with situation when there is a surrogate word
   73|       |    //    at the end of a chunk.
   74|   477k|    const __m256i surrogates_bytemask =
   75|   477k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800);
   76|       |
   77|       |    // bitmask = 0x0000 if there are no surrogates
   78|       |    //         = 0xc000 if the last word is a surrogate
   79|   477k|    const uint32_t surrogates_bitmask =
   80|   477k|        static_cast<uint32_t>(_mm256_movemask_epi8(surrogates_bytemask));
   81|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
   82|       |    // However, it is likely an uncommon occurrence.
   83|   477k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (83:9): [True: 465k, False: 11.6k]
  ------------------
   84|       |      // case: we extend all sixteen 16-bit code units to sixteen 32-bit code
   85|       |      // units
   86|   465k|      _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output),
   87|   465k|                          _mm256_cvtepu16_epi32(_mm256_castsi256_si128(in)));
   88|   465k|      _mm256_storeu_si256(
   89|   465k|          reinterpret_cast<__m256i *>(utf32_output + 8),
   90|   465k|          _mm256_cvtepu16_epi32(_mm256_extractf128_si256(in, 1)));
   91|   465k|      utf32_output += 16;
   92|   465k|      buf += 16;
   93|       |      // surrogate pair(s) in a register
   94|   465k|    } else {
   95|       |      // Let us do a scalar fallback.
   96|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
   97|       |      // in the presence of surrogate pairs may require non-trivial tables.
   98|  11.6k|      size_t forward = 15;
   99|  11.6k|      size_t k = 0;
  100|  11.6k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (100:11): [True: 0, False: 11.6k]
  ------------------
  101|      0|        forward = size_t(end - buf - 1);
  102|      0|      }
  103|   173k|      for (; k < forward; k++) {
  ------------------
  |  Branch (103:14): [True: 162k, False: 11.5k]
  ------------------
  104|   162k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  105|   162k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (105:13): [True: 149k, False: 12.1k]
  ------------------
  106|       |          // No surrogate pair
  107|   149k|          *utf32_output++ = char32_t(word);
  108|   149k|        } else {
  109|       |          // must be a surrogate pair
  110|  12.1k|          uint16_t diff = uint16_t(word - 0xD800);
  111|  12.1k|          uint16_t next_word =
  112|  12.1k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  113|  12.1k|          k++;
  114|  12.1k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  115|  12.1k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (115:15): [True: 82, False: 12.0k]
  ------------------
  116|     82|            return std::make_pair(nullptr, utf32_output);
  117|     82|          }
  118|  12.0k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  119|  12.0k|          *utf32_output++ = char32_t(value);
  120|  12.0k|        }
  121|   162k|      }
  122|  11.5k|      buf += k;
  123|  11.5k|    }
  124|   477k|  } // while
  125|    376|  return std::make_pair(buf, utf32_output);
  126|    458|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_139avx2_convert_utf16_to_utf32_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPDiEEPKDsmS7_:
  138|    442|                                        char32_t *utf32_output) {
  139|    442|  const char16_t *start = buf;
  140|    442|  const char16_t *end = buf + len;
  141|    442|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
  142|    442|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
  143|       |
  144|   202k|  while (end - buf >= 16) {
  ------------------
  |  Branch (144:10): [True: 202k, False: 306]
  ------------------
  145|   202k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
  146|   202k|    if (big_endian) {
  ------------------
  |  Branch (146:9): [Folded, False: 202k]
  ------------------
  147|      0|      const __m256i swap = _mm256_setr_epi8(
  148|      0|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
  149|      0|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
  150|      0|      in = _mm256_shuffle_epi8(in, swap);
  151|      0|    }
  152|       |
  153|       |    // 1. Check if there are any surrogate word in the input chunk.
  154|       |    //    We have also deal with situation when there is a surrogate word
  155|       |    //    at the end of a chunk.
  156|   202k|    const __m256i surrogates_bytemask =
  157|   202k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800);
  158|       |
  159|       |    // bitmask = 0x0000 if there are no surrogates
  160|       |    //         = 0xc000 if the last word is a surrogate
  161|   202k|    const uint32_t surrogates_bitmask =
  162|   202k|        static_cast<uint32_t>(_mm256_movemask_epi8(surrogates_bytemask));
  163|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
  164|       |    // However, it is likely an uncommon occurrence.
  165|   202k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (165:9): [True: 195k, False: 6.51k]
  ------------------
  166|       |      // case: we extend all sixteen 16-bit code units to sixteen 32-bit code
  167|       |      // units
  168|   195k|      _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output),
  169|   195k|                          _mm256_cvtepu16_epi32(_mm256_castsi256_si128(in)));
  170|   195k|      _mm256_storeu_si256(
  171|   195k|          reinterpret_cast<__m256i *>(utf32_output + 8),
  172|   195k|          _mm256_cvtepu16_epi32(_mm256_extractf128_si256(in, 1)));
  173|   195k|      utf32_output += 16;
  174|   195k|      buf += 16;
  175|       |      // surrogate pair(s) in a register
  176|   195k|    } else {
  177|       |      // Let us do a scalar fallback.
  178|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
  179|       |      // in the presence of surrogate pairs may require non-trivial tables.
  180|  6.51k|      size_t forward = 15;
  181|  6.51k|      size_t k = 0;
  182|  6.51k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (182:11): [True: 0, False: 6.51k]
  ------------------
  183|      0|        forward = size_t(end - buf - 1);
  184|      0|      }
  185|  92.7k|      for (; k < forward; k++) {
  ------------------
  |  Branch (185:14): [True: 86.4k, False: 6.37k]
  ------------------
  186|  86.4k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  187|  86.4k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (187:13): [True: 75.5k, False: 10.8k]
  ------------------
  188|       |          // No surrogate pair
  189|  75.5k|          *utf32_output++ = char32_t(word);
  190|  75.5k|        } else {
  191|       |          // must be a surrogate pair
  192|  10.8k|          uint16_t diff = uint16_t(word - 0xD800);
  193|  10.8k|          uint16_t next_word =
  194|  10.8k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  195|  10.8k|          k++;
  196|  10.8k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  197|  10.8k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (197:15): [True: 136, False: 10.7k]
  ------------------
  198|    136|            return std::make_pair(
  199|    136|                result(error_code::SURROGATE, buf - start + k - 1),
  200|    136|                utf32_output);
  201|    136|          }
  202|  10.7k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  203|  10.7k|          *utf32_output++ = char32_t(value);
  204|  10.7k|        }
  205|  86.4k|      }
  206|  6.37k|      buf += k;
  207|  6.37k|    }
  208|   202k|  } // while
  209|    306|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf32_output);
  210|    442|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_139avx2_convert_utf16_to_utf32_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPDiEEPKDsmS7_:
  138|    360|                                        char32_t *utf32_output) {
  139|    360|  const char16_t *start = buf;
  140|    360|  const char16_t *end = buf + len;
  141|    360|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
  142|    360|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
  143|       |
  144|   109k|  while (end - buf >= 16) {
  ------------------
  |  Branch (144:10): [True: 109k, False: 268]
  ------------------
  145|   109k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
  146|   109k|    if (big_endian) {
  ------------------
  |  Branch (146:9): [True: 109k, Folded]
  ------------------
  147|   109k|      const __m256i swap = _mm256_setr_epi8(
  148|   109k|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
  149|   109k|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
  150|   109k|      in = _mm256_shuffle_epi8(in, swap);
  151|   109k|    }
  152|       |
  153|       |    // 1. Check if there are any surrogate word in the input chunk.
  154|       |    //    We have also deal with situation when there is a surrogate word
  155|       |    //    at the end of a chunk.
  156|   109k|    const __m256i surrogates_bytemask =
  157|   109k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800);
  158|       |
  159|       |    // bitmask = 0x0000 if there are no surrogates
  160|       |    //         = 0xc000 if the last word is a surrogate
  161|   109k|    const uint32_t surrogates_bitmask =
  162|   109k|        static_cast<uint32_t>(_mm256_movemask_epi8(surrogates_bytemask));
  163|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
  164|       |    // However, it is likely an uncommon occurrence.
  165|   109k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (165:9): [True: 102k, False: 6.20k]
  ------------------
  166|       |      // case: we extend all sixteen 16-bit code units to sixteen 32-bit code
  167|       |      // units
  168|   102k|      _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output),
  169|   102k|                          _mm256_cvtepu16_epi32(_mm256_castsi256_si128(in)));
  170|   102k|      _mm256_storeu_si256(
  171|   102k|          reinterpret_cast<__m256i *>(utf32_output + 8),
  172|   102k|          _mm256_cvtepu16_epi32(_mm256_extractf128_si256(in, 1)));
  173|   102k|      utf32_output += 16;
  174|   102k|      buf += 16;
  175|       |      // surrogate pair(s) in a register
  176|   102k|    } else {
  177|       |      // Let us do a scalar fallback.
  178|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
  179|       |      // in the presence of surrogate pairs may require non-trivial tables.
  180|  6.20k|      size_t forward = 15;
  181|  6.20k|      size_t k = 0;
  182|  6.20k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (182:11): [True: 0, False: 6.20k]
  ------------------
  183|      0|        forward = size_t(end - buf - 1);
  184|      0|      }
  185|  88.6k|      for (; k < forward; k++) {
  ------------------
  |  Branch (185:14): [True: 82.5k, False: 6.11k]
  ------------------
  186|  82.5k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  187|  82.5k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (187:13): [True: 71.8k, False: 10.6k]
  ------------------
  188|       |          // No surrogate pair
  189|  71.8k|          *utf32_output++ = char32_t(word);
  190|  71.8k|        } else {
  191|       |          // must be a surrogate pair
  192|  10.6k|          uint16_t diff = uint16_t(word - 0xD800);
  193|  10.6k|          uint16_t next_word =
  194|  10.6k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  195|  10.6k|          k++;
  196|  10.6k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  197|  10.6k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (197:15): [True: 92, False: 10.5k]
  ------------------
  198|     92|            return std::make_pair(
  199|     92|                result(error_code::SURROGATE, buf - start + k - 1),
  200|     92|                utf32_output);
  201|     92|          }
  202|  10.5k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  203|  10.5k|          *utf32_output++ = char32_t(value);
  204|  10.5k|        }
  205|  82.5k|      }
  206|  6.11k|      buf += k;
  207|  6.11k|    }
  208|   109k|  } // while
  209|    268|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf32_output);
  210|    360|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_126avx2_convert_utf16_to_utf8ILNS_10endiannessE0EEENSt3__14pairIPKDsPcEES7_mS8_:
   56|    874|avx2_convert_utf16_to_utf8(const char16_t *buf, size_t len, char *utf8_output) {
   57|    874|  const char16_t *end = buf + len;
   58|    874|  const __m256i v_0000 = _mm256_setzero_si256();
   59|    874|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
   60|    874|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
   61|    874|  const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
   62|    874|  const size_t safety_margin =
   63|    874|      12; // to avoid overruns, see issue
   64|       |          // https://github.com/simdutf/simdutf/issues/92
   65|       |
   66|  1.02M|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (66:10): [True: 1.02M, False: 746]
  ------------------
   67|  1.02M|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
   68|  1.02M|    if (big_endian) {
  ------------------
  |  Branch (68:9): [Folded, False: 1.02M]
  ------------------
   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|  1.02M|    const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
   76|  1.02M|    if (_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (76:9): [True: 826k, False: 201k]
  ------------------
   77|       |      // 1. pack the bytes
   78|   826k|      const __m128i utf8_packed = _mm_packus_epi16(
   79|   826k|          _mm256_castsi256_si128(in), _mm256_extractf128_si256(in, 1));
   80|       |      // 2. store (16 bytes)
   81|   826k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   82|       |      // 3. adjust pointers
   83|   826k|      buf += 16;
   84|   826k|      utf8_output += 16;
   85|   826k|      continue; // we are done for this round!
   86|   826k|    }
   87|       |    // no bits set above 7th bit
   88|   201k|    const __m256i one_byte_bytemask =
   89|   201k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
   90|   201k|    const uint32_t one_byte_bitmask =
   91|   201k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
   92|       |
   93|       |    // no bits set above 11th bit
   94|   201k|    const __m256i one_or_two_bytes_bytemask =
   95|   201k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000);
   96|   201k|    const uint32_t one_or_two_bytes_bitmask =
   97|   201k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
   98|   201k|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (98:9): [True: 37.0k, False: 164k]
  ------------------
   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|  37.0k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
  104|  37.0k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
  105|       |
  106|       |      // t0 = [000a|aaaa|bbbb|bb00]
  107|  37.0k|      const __m256i t0 = _mm256_slli_epi16(in, 2);
  108|       |      // t1 = [000a|aaaa|0000|0000]
  109|  37.0k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
  110|       |      // t2 = [0000|0000|00bb|bbbb]
  111|  37.0k|      const __m256i t2 = _mm256_and_si256(in, v_003f);
  112|       |      // t3 = [000a|aaaa|00bb|bbbb]
  113|  37.0k|      const __m256i t3 = _mm256_or_si256(t1, t2);
  114|       |      // t4 = [110a|aaaa|10bb|bbbb]
  115|  37.0k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
  116|       |
  117|       |      // 2. merge ASCII and 2-byte codewords
  118|  37.0k|      const __m256i utf8_unpacked =
  119|  37.0k|          _mm256_blendv_epi8(t4, in, one_byte_bytemask);
  120|       |
  121|       |      // 3. prepare bitmask for 8-bit lookup
  122|  37.0k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
  123|  37.0k|      const uint32_t M1 = M0 >> 7;
  124|  37.0k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
  125|       |      // 4. pack the bytes
  126|       |
  127|  37.0k|      const uint8_t *row =
  128|  37.0k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
  129|  37.0k|      const uint8_t *row_2 =
  130|  37.0k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
  131|  37.0k|                                                                       16)][0];
  132|       |
  133|  37.0k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  134|  37.0k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
  135|       |
  136|  37.0k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
  137|  37.0k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|  37.0k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|  37.0k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  138|       |      // 5. store bytes
  139|  37.0k|      _mm_storeu_si128((__m128i *)utf8_output,
  140|  37.0k|                       _mm256_castsi256_si128(utf8_packed));
  141|  37.0k|      utf8_output += row[0];
  142|  37.0k|      _mm_storeu_si128((__m128i *)utf8_output,
  143|  37.0k|                       _mm256_extractf128_si256(utf8_packed, 1));
  144|  37.0k|      utf8_output += row_2[0];
  145|       |
  146|       |      // 6. adjust pointers
  147|  37.0k|      buf += 16;
  148|  37.0k|      continue;
  149|  37.0k|    }
  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|   164k|    const __m256i surrogates_bytemask =
  154|   164k|        _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|   164k|    const uint32_t surrogates_bitmask =
  159|   164k|        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|   164k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (162:9): [True: 155k, False: 9.24k]
  ------------------
  163|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  164|   155k|      const __m256i dup_even = _mm256_setr_epi16(
  165|   155k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  166|   155k|          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|   155k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  196|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  197|   155k|      const __m256i t0 = _mm256_shuffle_epi8(in, dup_even);
  198|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  199|   155k|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  195|   155k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  200|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  201|   155k|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  195|   155k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  202|       |
  203|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  204|   155k|      const __m256i s0 = _mm256_srli_epi16(in, 4);
  205|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  206|   155k|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  195|   155k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  207|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  208|   155k|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  195|   155k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  209|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  210|   155k|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  195|   155k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  211|   155k|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  212|   155k|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  195|   155k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  213|   155k|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  214|   155k|#undef simdutf_vec
  215|       |
  216|       |      // 4. expand code units 16-bit => 32-bit
  217|   155k|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  218|   155k|      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|   155k|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  222|   155k|                            (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|   155k|      const uint8_t mask0 = uint8_t(mask);
  243|   155k|      const uint8_t *row0 =
  244|   155k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  245|   155k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  246|   155k|      const __m128i utf8_0 =
  247|   155k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  248|       |
  249|   155k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  250|   155k|      const uint8_t *row1 =
  251|   155k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  252|   155k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  253|   155k|      const __m128i utf8_1 =
  254|   155k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  255|       |
  256|   155k|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  257|   155k|      const uint8_t *row2 =
  258|   155k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  259|   155k|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  260|   155k|      const __m128i utf8_2 =
  261|   155k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  262|       |
  263|   155k|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  264|   155k|      const uint8_t *row3 =
  265|   155k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  266|   155k|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  267|   155k|      const __m128i utf8_3 =
  268|   155k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  269|       |
  270|   155k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  271|   155k|      utf8_output += row0[0];
  272|   155k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  273|   155k|      utf8_output += row1[0];
  274|   155k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  275|   155k|      utf8_output += row2[0];
  276|   155k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  277|   155k|      utf8_output += row3[0];
  278|   155k|      buf += 16;
  279|       |      // surrogate pair(s) in a register
  280|   155k|    } 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|  9.24k|      size_t forward = 15;
  285|  9.24k|      size_t k = 0;
  286|  9.24k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (286:11): [True: 0, False: 9.24k]
  ------------------
  287|      0|        forward = size_t(end - buf - 1);
  288|      0|      }
  289|   136k|      for (; k < forward; k++) {
  ------------------
  |  Branch (289:14): [True: 127k, False: 9.11k]
  ------------------
  290|   127k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  291|   127k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (291:13): [True: 15.3k, False: 112k]
  ------------------
  292|  15.3k|          *utf8_output++ = char(word);
  293|   112k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (293:20): [True: 5.41k, False: 106k]
  ------------------
  294|  5.41k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  295|  5.41k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  296|   106k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (296:20): [True: 96.5k, False: 10.2k]
  ------------------
  297|  96.5k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  298|  96.5k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  299|  96.5k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  300|  96.5k|        } else {
  301|       |          // must be a surrogate pair
  302|  10.2k|          uint16_t diff = uint16_t(word - 0xD800);
  303|  10.2k|          uint16_t next_word =
  304|  10.2k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  305|  10.2k|          k++;
  306|  10.2k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  307|  10.2k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (307:15): [True: 128, False: 10.1k]
  ------------------
  308|    128|            return std::make_pair(nullptr, utf8_output);
  309|    128|          }
  310|  10.1k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  311|  10.1k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  312|  10.1k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  313|  10.1k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  314|  10.1k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  315|  10.1k|        }
  316|   127k|      }
  317|  9.11k|      buf += k;
  318|  9.11k|    }
  319|   164k|  } // while
  320|    746|  return std::make_pair(buf, utf8_output);
  321|    874|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_126avx2_convert_utf16_to_utf8ILNS_10endiannessE1EEENSt3__14pairIPKDsPcEES7_mS8_:
   56|    846|avx2_convert_utf16_to_utf8(const char16_t *buf, size_t len, char *utf8_output) {
   57|    846|  const char16_t *end = buf + len;
   58|    846|  const __m256i v_0000 = _mm256_setzero_si256();
   59|    846|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
   60|    846|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
   61|    846|  const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
   62|    846|  const size_t safety_margin =
   63|    846|      12; // to avoid overruns, see issue
   64|       |          // https://github.com/simdutf/simdutf/issues/92
   65|       |
   66|  1.11M|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (66:10): [True: 1.11M, False: 730]
  ------------------
   67|  1.11M|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
   68|  1.11M|    if (big_endian) {
  ------------------
  |  Branch (68:9): [True: 1.11M, Folded]
  ------------------
   69|  1.11M|      const __m256i swap = _mm256_setr_epi8(
   70|  1.11M|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   71|  1.11M|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   72|  1.11M|      in = _mm256_shuffle_epi8(in, swap);
   73|  1.11M|    }
   74|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
   75|  1.11M|    const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
   76|  1.11M|    if (_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (76:9): [True: 843k, False: 268k]
  ------------------
   77|       |      // 1. pack the bytes
   78|   843k|      const __m128i utf8_packed = _mm_packus_epi16(
   79|   843k|          _mm256_castsi256_si128(in), _mm256_extractf128_si256(in, 1));
   80|       |      // 2. store (16 bytes)
   81|   843k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   82|       |      // 3. adjust pointers
   83|   843k|      buf += 16;
   84|   843k|      utf8_output += 16;
   85|   843k|      continue; // we are done for this round!
   86|   843k|    }
   87|       |    // no bits set above 7th bit
   88|   268k|    const __m256i one_byte_bytemask =
   89|   268k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
   90|   268k|    const uint32_t one_byte_bitmask =
   91|   268k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
   92|       |
   93|       |    // no bits set above 11th bit
   94|   268k|    const __m256i one_or_two_bytes_bytemask =
   95|   268k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000);
   96|   268k|    const uint32_t one_or_two_bytes_bitmask =
   97|   268k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
   98|   268k|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (98:9): [True: 27.8k, False: 241k]
  ------------------
   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|  27.8k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
  104|  27.8k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
  105|       |
  106|       |      // t0 = [000a|aaaa|bbbb|bb00]
  107|  27.8k|      const __m256i t0 = _mm256_slli_epi16(in, 2);
  108|       |      // t1 = [000a|aaaa|0000|0000]
  109|  27.8k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
  110|       |      // t2 = [0000|0000|00bb|bbbb]
  111|  27.8k|      const __m256i t2 = _mm256_and_si256(in, v_003f);
  112|       |      // t3 = [000a|aaaa|00bb|bbbb]
  113|  27.8k|      const __m256i t3 = _mm256_or_si256(t1, t2);
  114|       |      // t4 = [110a|aaaa|10bb|bbbb]
  115|  27.8k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
  116|       |
  117|       |      // 2. merge ASCII and 2-byte codewords
  118|  27.8k|      const __m256i utf8_unpacked =
  119|  27.8k|          _mm256_blendv_epi8(t4, in, one_byte_bytemask);
  120|       |
  121|       |      // 3. prepare bitmask for 8-bit lookup
  122|  27.8k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
  123|  27.8k|      const uint32_t M1 = M0 >> 7;
  124|  27.8k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
  125|       |      // 4. pack the bytes
  126|       |
  127|  27.8k|      const uint8_t *row =
  128|  27.8k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
  129|  27.8k|      const uint8_t *row_2 =
  130|  27.8k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
  131|  27.8k|                                                                       16)][0];
  132|       |
  133|  27.8k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  134|  27.8k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
  135|       |
  136|  27.8k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
  137|  27.8k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|  27.8k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|  27.8k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  138|       |      // 5. store bytes
  139|  27.8k|      _mm_storeu_si128((__m128i *)utf8_output,
  140|  27.8k|                       _mm256_castsi256_si128(utf8_packed));
  141|  27.8k|      utf8_output += row[0];
  142|  27.8k|      _mm_storeu_si128((__m128i *)utf8_output,
  143|  27.8k|                       _mm256_extractf128_si256(utf8_packed, 1));
  144|  27.8k|      utf8_output += row_2[0];
  145|       |
  146|       |      // 6. adjust pointers
  147|  27.8k|      buf += 16;
  148|  27.8k|      continue;
  149|  27.8k|    }
  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|   241k|    const __m256i surrogates_bytemask =
  154|   241k|        _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|   241k|    const uint32_t surrogates_bitmask =
  159|   241k|        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|   241k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (162:9): [True: 229k, False: 11.8k]
  ------------------
  163|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  164|   229k|      const __m256i dup_even = _mm256_setr_epi16(
  165|   229k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  166|   229k|          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|   229k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  196|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  197|   229k|      const __m256i t0 = _mm256_shuffle_epi8(in, dup_even);
  198|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  199|   229k|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  195|   229k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  200|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  201|   229k|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  195|   229k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  202|       |
  203|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  204|   229k|      const __m256i s0 = _mm256_srli_epi16(in, 4);
  205|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  206|   229k|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  195|   229k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  207|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  208|   229k|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  195|   229k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  209|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  210|   229k|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  195|   229k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  211|   229k|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  212|   229k|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  195|   229k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  213|   229k|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  214|   229k|#undef simdutf_vec
  215|       |
  216|       |      // 4. expand code units 16-bit => 32-bit
  217|   229k|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  218|   229k|      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|   229k|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  222|   229k|                            (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|   229k|      const uint8_t mask0 = uint8_t(mask);
  243|   229k|      const uint8_t *row0 =
  244|   229k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  245|   229k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  246|   229k|      const __m128i utf8_0 =
  247|   229k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  248|       |
  249|   229k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  250|   229k|      const uint8_t *row1 =
  251|   229k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  252|   229k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  253|   229k|      const __m128i utf8_1 =
  254|   229k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  255|       |
  256|   229k|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  257|   229k|      const uint8_t *row2 =
  258|   229k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  259|   229k|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  260|   229k|      const __m128i utf8_2 =
  261|   229k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  262|       |
  263|   229k|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  264|   229k|      const uint8_t *row3 =
  265|   229k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  266|   229k|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  267|   229k|      const __m128i utf8_3 =
  268|   229k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  269|       |
  270|   229k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  271|   229k|      utf8_output += row0[0];
  272|   229k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  273|   229k|      utf8_output += row1[0];
  274|   229k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  275|   229k|      utf8_output += row2[0];
  276|   229k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  277|   229k|      utf8_output += row3[0];
  278|   229k|      buf += 16;
  279|       |      // surrogate pair(s) in a register
  280|   229k|    } 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|  11.8k|      size_t forward = 15;
  285|  11.8k|      size_t k = 0;
  286|  11.8k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (286:11): [True: 0, False: 11.8k]
  ------------------
  287|      0|        forward = size_t(end - buf - 1);
  288|      0|      }
  289|   176k|      for (; k < forward; k++) {
  ------------------
  |  Branch (289:14): [True: 164k, False: 11.7k]
  ------------------
  290|   164k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  291|   164k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (291:13): [True: 19.6k, False: 145k]
  ------------------
  292|  19.6k|          *utf8_output++ = char(word);
  293|   145k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (293:20): [True: 4.80k, False: 140k]
  ------------------
  294|  4.80k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  295|  4.80k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  296|   140k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (296:20): [True: 127k, False: 12.9k]
  ------------------
  297|   127k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  298|   127k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  299|   127k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  300|   127k|        } else {
  301|       |          // must be a surrogate pair
  302|  12.9k|          uint16_t diff = uint16_t(word - 0xD800);
  303|  12.9k|          uint16_t next_word =
  304|  12.9k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  305|  12.9k|          k++;
  306|  12.9k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  307|  12.9k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (307:15): [True: 116, False: 12.8k]
  ------------------
  308|    116|            return std::make_pair(nullptr, utf8_output);
  309|    116|          }
  310|  12.8k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  311|  12.8k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  312|  12.8k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  313|  12.8k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  314|  12.8k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  315|  12.8k|        }
  316|   164k|      }
  317|  11.7k|      buf += k;
  318|  11.7k|    }
  319|   241k|  } // while
  320|    730|  return std::make_pair(buf, utf8_output);
  321|    846|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_138avx2_convert_utf16_to_utf8_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
  333|    686|                                       char *utf8_output) {
  334|    686|  const char16_t *start = buf;
  335|    686|  const char16_t *end = buf + len;
  336|       |
  337|    686|  const __m256i v_0000 = _mm256_setzero_si256();
  338|    686|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
  339|    686|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
  340|    686|  const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
  341|    686|  const size_t safety_margin =
  342|    686|      12; // to avoid overruns, see issue
  343|       |          // https://github.com/simdutf/simdutf/issues/92
  344|       |
  345|   735k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (345:10): [True: 734k, False: 576]
  ------------------
  346|   734k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
  347|   734k|    if (big_endian) {
  ------------------
  |  Branch (347:9): [Folded, False: 734k]
  ------------------
  348|      0|      const __m256i swap = _mm256_setr_epi8(
  349|      0|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
  350|      0|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
  351|      0|      in = _mm256_shuffle_epi8(in, swap);
  352|      0|    }
  353|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
  354|   734k|    const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
  355|   734k|    if (_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (355:9): [True: 304k, False: 430k]
  ------------------
  356|       |      // 1. pack the bytes
  357|   304k|      const __m128i utf8_packed = _mm_packus_epi16(
  358|   304k|          _mm256_castsi256_si128(in), _mm256_extractf128_si256(in, 1));
  359|       |      // 2. store (16 bytes)
  360|   304k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  361|       |      // 3. adjust pointers
  362|   304k|      buf += 16;
  363|   304k|      utf8_output += 16;
  364|   304k|      continue; // we are done for this round!
  365|   304k|    }
  366|       |    // no bits set above 7th bit
  367|   430k|    const __m256i one_byte_bytemask =
  368|   430k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
  369|   430k|    const uint32_t one_byte_bitmask =
  370|   430k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
  371|       |
  372|       |    // no bits set above 11th bit
  373|   430k|    const __m256i one_or_two_bytes_bytemask =
  374|   430k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000);
  375|   430k|    const uint32_t one_or_two_bytes_bitmask =
  376|   430k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
  377|   430k|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (377:9): [True: 52.9k, False: 377k]
  ------------------
  378|       |
  379|       |      // 1. prepare 2-byte values
  380|       |      // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
  381|       |      // expected output   : [110a|aaaa|10bb|bbbb] x 8
  382|  52.9k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
  383|  52.9k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
  384|       |
  385|       |      // t0 = [000a|aaaa|bbbb|bb00]
  386|  52.9k|      const __m256i t0 = _mm256_slli_epi16(in, 2);
  387|       |      // t1 = [000a|aaaa|0000|0000]
  388|  52.9k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
  389|       |      // t2 = [0000|0000|00bb|bbbb]
  390|  52.9k|      const __m256i t2 = _mm256_and_si256(in, v_003f);
  391|       |      // t3 = [000a|aaaa|00bb|bbbb]
  392|  52.9k|      const __m256i t3 = _mm256_or_si256(t1, t2);
  393|       |      // t4 = [110a|aaaa|10bb|bbbb]
  394|  52.9k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
  395|       |
  396|       |      // 2. merge ASCII and 2-byte codewords
  397|  52.9k|      const __m256i utf8_unpacked =
  398|  52.9k|          _mm256_blendv_epi8(t4, in, one_byte_bytemask);
  399|       |
  400|       |      // 3. prepare bitmask for 8-bit lookup
  401|  52.9k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
  402|  52.9k|      const uint32_t M1 = M0 >> 7;
  403|  52.9k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
  404|       |      // 4. pack the bytes
  405|       |
  406|  52.9k|      const uint8_t *row =
  407|  52.9k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
  408|  52.9k|      const uint8_t *row_2 =
  409|  52.9k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
  410|  52.9k|                                                                       16)][0];
  411|       |
  412|  52.9k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  413|  52.9k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
  414|       |
  415|  52.9k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
  416|  52.9k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|  52.9k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|  52.9k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  417|       |      // 5. store bytes
  418|  52.9k|      _mm_storeu_si128((__m128i *)utf8_output,
  419|  52.9k|                       _mm256_castsi256_si128(utf8_packed));
  420|  52.9k|      utf8_output += row[0];
  421|  52.9k|      _mm_storeu_si128((__m128i *)utf8_output,
  422|  52.9k|                       _mm256_extractf128_si256(utf8_packed, 1));
  423|  52.9k|      utf8_output += row_2[0];
  424|       |
  425|       |      // 6. adjust pointers
  426|  52.9k|      buf += 16;
  427|  52.9k|      continue;
  428|  52.9k|    }
  429|       |    // 1. Check if there are any surrogate word in the input chunk.
  430|       |    //    We have also deal with situation when there is a surrogate word
  431|       |    //    at the end of a chunk.
  432|   377k|    const __m256i surrogates_bytemask =
  433|   377k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800);
  434|       |
  435|       |    // bitmask = 0x0000 if there are no surrogates
  436|       |    //         = 0xc000 if the last word is a surrogate
  437|   377k|    const uint32_t surrogates_bitmask =
  438|   377k|        static_cast<uint32_t>(_mm256_movemask_epi8(surrogates_bytemask));
  439|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
  440|       |    // However, it is likely an uncommon occurrence.
  441|   377k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (441:9): [True: 371k, False: 5.40k]
  ------------------
  442|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  443|   371k|      const __m256i dup_even = _mm256_setr_epi16(
  444|   371k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  445|   371k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
  446|       |
  447|       |      /* In this branch we handle three cases:
  448|       |         1. [0000|0000|0ccc|cccc] => [0ccc|cccc]                           -
  449|       |        single UFT-8 byte
  450|       |         2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc]              - two
  451|       |        UTF-8 bytes
  452|       |         3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] -
  453|       |        three UTF-8 bytes
  454|       |
  455|       |        We expand the input word (16-bit) into two code units (32-bit), thus
  456|       |        we have room for four bytes. However, we need five distinct bit
  457|       |        layouts. Note that the last byte in cases #2 and #3 is the same.
  458|       |
  459|       |        We precompute byte 1 for case #1 and the common byte for cases #2 & #3
  460|       |        in register t2.
  461|       |
  462|       |        We precompute byte 1 for case #3 and -- **conditionally** -- precompute
  463|       |        either byte 1 for case #2 or byte 2 for case #3. Note that they
  464|       |        differ by exactly one bit.
  465|       |
  466|       |        Finally from these two code units we build proper UTF-8 sequence, taking
  467|       |        into account the case (i.e, the number of bytes to write).
  468|       |      */
  469|       |      /**
  470|       |       * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
  471|       |       * t2 => [0ccc|cccc] [10cc|cccc]
  472|       |       * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
  473|       |       */
  474|   371k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  475|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  476|   371k|      const __m256i t0 = _mm256_shuffle_epi8(in, dup_even);
  477|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  478|   371k|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  474|   371k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  479|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  480|   371k|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  474|   371k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  481|       |
  482|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  483|   371k|      const __m256i s0 = _mm256_srli_epi16(in, 4);
  484|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  485|   371k|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  474|   371k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  486|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  487|   371k|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  474|   371k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  488|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  489|   371k|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  474|   371k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  490|   371k|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  491|   371k|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  474|   371k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  492|   371k|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  493|   371k|#undef simdutf_vec
  494|       |
  495|       |      // 4. expand code units 16-bit => 32-bit
  496|   371k|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  497|   371k|      const __m256i out1 = _mm256_unpackhi_epi16(t2, s4);
  498|       |
  499|       |      // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
  500|   371k|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  501|   371k|                            (one_or_two_bytes_bitmask & 0xaaaaaaaa);
  502|       |      // Due to the wider registers, the following path is less likely to be
  503|       |      // useful.
  504|       |      /*if(mask == 0) {
  505|       |        // We only have three-byte code units. Use fast path.
  506|       |        const __m256i shuffle =
  507|       |      _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1,
  508|       |      2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); const __m256i utf8_0 =
  509|       |      _mm256_shuffle_epi8(out0, shuffle); const __m256i utf8_1 =
  510|       |      _mm256_shuffle_epi8(out1, shuffle);
  511|       |        _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0));
  512|       |        utf8_output += 12;
  513|       |        _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1));
  514|       |        utf8_output += 12;
  515|       |        _mm_storeu_si128((__m128i*)utf8_output,
  516|       |      _mm256_extractf128_si256(utf8_0,1)); utf8_output += 12;
  517|       |        _mm_storeu_si128((__m128i*)utf8_output,
  518|       |      _mm256_extractf128_si256(utf8_1,1)); utf8_output += 12; buf += 16;
  519|       |        continue;
  520|       |      }*/
  521|   371k|      const uint8_t mask0 = uint8_t(mask);
  522|   371k|      const uint8_t *row0 =
  523|   371k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  524|   371k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  525|   371k|      const __m128i utf8_0 =
  526|   371k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  527|       |
  528|   371k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  529|   371k|      const uint8_t *row1 =
  530|   371k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  531|   371k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  532|   371k|      const __m128i utf8_1 =
  533|   371k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  534|       |
  535|   371k|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  536|   371k|      const uint8_t *row2 =
  537|   371k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  538|   371k|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  539|   371k|      const __m128i utf8_2 =
  540|   371k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  541|       |
  542|   371k|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  543|   371k|      const uint8_t *row3 =
  544|   371k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  545|   371k|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  546|   371k|      const __m128i utf8_3 =
  547|   371k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  548|       |
  549|   371k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  550|   371k|      utf8_output += row0[0];
  551|   371k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  552|   371k|      utf8_output += row1[0];
  553|   371k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  554|   371k|      utf8_output += row2[0];
  555|   371k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  556|   371k|      utf8_output += row3[0];
  557|   371k|      buf += 16;
  558|       |      // surrogate pair(s) in a register
  559|   371k|    } else {
  560|       |      // Let us do a scalar fallback.
  561|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
  562|       |      // in the presence of surrogate pairs may require non-trivial tables.
  563|  5.40k|      size_t forward = 15;
  564|  5.40k|      size_t k = 0;
  565|  5.40k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (565:11): [True: 0, False: 5.40k]
  ------------------
  566|      0|        forward = size_t(end - buf - 1);
  567|      0|      }
  568|  79.6k|      for (; k < forward; k++) {
  ------------------
  |  Branch (568:14): [True: 74.3k, False: 5.29k]
  ------------------
  569|  74.3k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  570|  74.3k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (570:13): [True: 10.8k, False: 63.5k]
  ------------------
  571|  10.8k|          *utf8_output++ = char(word);
  572|  63.5k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (572:20): [True: 3.66k, False: 59.9k]
  ------------------
  573|  3.66k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  574|  3.66k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  575|  59.9k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (575:20): [True: 53.7k, False: 6.14k]
  ------------------
  576|  53.7k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  577|  53.7k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  578|  53.7k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  579|  53.7k|        } else {
  580|       |          // must be a surrogate pair
  581|  6.14k|          uint16_t diff = uint16_t(word - 0xD800);
  582|  6.14k|          uint16_t next_word =
  583|  6.14k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  584|  6.14k|          k++;
  585|  6.14k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  586|  6.14k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (586:15): [True: 110, False: 6.03k]
  ------------------
  587|    110|            return std::make_pair(
  588|    110|                result(error_code::SURROGATE, buf - start + k - 1),
  589|    110|                utf8_output);
  590|    110|          }
  591|  6.03k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  592|  6.03k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  593|  6.03k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  594|  6.03k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  595|  6.03k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  596|  6.03k|        }
  597|  74.3k|      }
  598|  5.29k|      buf += k;
  599|  5.29k|    }
  600|   377k|  } // while
  601|    576|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  602|    686|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_138avx2_convert_utf16_to_utf8_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
  333|    556|                                       char *utf8_output) {
  334|    556|  const char16_t *start = buf;
  335|    556|  const char16_t *end = buf + len;
  336|       |
  337|    556|  const __m256i v_0000 = _mm256_setzero_si256();
  338|    556|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
  339|    556|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
  340|    556|  const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
  341|    556|  const size_t safety_margin =
  342|    556|      12; // to avoid overruns, see issue
  343|       |          // https://github.com/simdutf/simdutf/issues/92
  344|       |
  345|   396k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (345:10): [True: 396k, False: 410]
  ------------------
  346|   396k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
  347|   396k|    if (big_endian) {
  ------------------
  |  Branch (347:9): [True: 396k, Folded]
  ------------------
  348|   396k|      const __m256i swap = _mm256_setr_epi8(
  349|   396k|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
  350|   396k|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
  351|   396k|      in = _mm256_shuffle_epi8(in, swap);
  352|   396k|    }
  353|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
  354|   396k|    const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
  355|   396k|    if (_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (355:9): [True: 297k, False: 99.0k]
  ------------------
  356|       |      // 1. pack the bytes
  357|   297k|      const __m128i utf8_packed = _mm_packus_epi16(
  358|   297k|          _mm256_castsi256_si128(in), _mm256_extractf128_si256(in, 1));
  359|       |      // 2. store (16 bytes)
  360|   297k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  361|       |      // 3. adjust pointers
  362|   297k|      buf += 16;
  363|   297k|      utf8_output += 16;
  364|   297k|      continue; // we are done for this round!
  365|   297k|    }
  366|       |    // no bits set above 7th bit
  367|  99.0k|    const __m256i one_byte_bytemask =
  368|  99.0k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
  369|  99.0k|    const uint32_t one_byte_bitmask =
  370|  99.0k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
  371|       |
  372|       |    // no bits set above 11th bit
  373|  99.0k|    const __m256i one_or_two_bytes_bytemask =
  374|  99.0k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000);
  375|  99.0k|    const uint32_t one_or_two_bytes_bitmask =
  376|  99.0k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
  377|  99.0k|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (377:9): [True: 26.3k, False: 72.6k]
  ------------------
  378|       |
  379|       |      // 1. prepare 2-byte values
  380|       |      // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
  381|       |      // expected output   : [110a|aaaa|10bb|bbbb] x 8
  382|  26.3k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
  383|  26.3k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
  384|       |
  385|       |      // t0 = [000a|aaaa|bbbb|bb00]
  386|  26.3k|      const __m256i t0 = _mm256_slli_epi16(in, 2);
  387|       |      // t1 = [000a|aaaa|0000|0000]
  388|  26.3k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
  389|       |      // t2 = [0000|0000|00bb|bbbb]
  390|  26.3k|      const __m256i t2 = _mm256_and_si256(in, v_003f);
  391|       |      // t3 = [000a|aaaa|00bb|bbbb]
  392|  26.3k|      const __m256i t3 = _mm256_or_si256(t1, t2);
  393|       |      // t4 = [110a|aaaa|10bb|bbbb]
  394|  26.3k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
  395|       |
  396|       |      // 2. merge ASCII and 2-byte codewords
  397|  26.3k|      const __m256i utf8_unpacked =
  398|  26.3k|          _mm256_blendv_epi8(t4, in, one_byte_bytemask);
  399|       |
  400|       |      // 3. prepare bitmask for 8-bit lookup
  401|  26.3k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
  402|  26.3k|      const uint32_t M1 = M0 >> 7;
  403|  26.3k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
  404|       |      // 4. pack the bytes
  405|       |
  406|  26.3k|      const uint8_t *row =
  407|  26.3k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
  408|  26.3k|      const uint8_t *row_2 =
  409|  26.3k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
  410|  26.3k|                                                                       16)][0];
  411|       |
  412|  26.3k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  413|  26.3k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
  414|       |
  415|  26.3k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
  416|  26.3k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|  26.3k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|  26.3k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  417|       |      // 5. store bytes
  418|  26.3k|      _mm_storeu_si128((__m128i *)utf8_output,
  419|  26.3k|                       _mm256_castsi256_si128(utf8_packed));
  420|  26.3k|      utf8_output += row[0];
  421|  26.3k|      _mm_storeu_si128((__m128i *)utf8_output,
  422|  26.3k|                       _mm256_extractf128_si256(utf8_packed, 1));
  423|  26.3k|      utf8_output += row_2[0];
  424|       |
  425|       |      // 6. adjust pointers
  426|  26.3k|      buf += 16;
  427|  26.3k|      continue;
  428|  26.3k|    }
  429|       |    // 1. Check if there are any surrogate word in the input chunk.
  430|       |    //    We have also deal with situation when there is a surrogate word
  431|       |    //    at the end of a chunk.
  432|  72.6k|    const __m256i surrogates_bytemask =
  433|  72.6k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800);
  434|       |
  435|       |    // bitmask = 0x0000 if there are no surrogates
  436|       |    //         = 0xc000 if the last word is a surrogate
  437|  72.6k|    const uint32_t surrogates_bitmask =
  438|  72.6k|        static_cast<uint32_t>(_mm256_movemask_epi8(surrogates_bytemask));
  439|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
  440|       |    // However, it is likely an uncommon occurrence.
  441|  72.6k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (441:9): [True: 65.3k, False: 7.36k]
  ------------------
  442|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  443|  65.3k|      const __m256i dup_even = _mm256_setr_epi16(
  444|  65.3k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  445|  65.3k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
  446|       |
  447|       |      /* In this branch we handle three cases:
  448|       |         1. [0000|0000|0ccc|cccc] => [0ccc|cccc]                           -
  449|       |        single UFT-8 byte
  450|       |         2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc]              - two
  451|       |        UTF-8 bytes
  452|       |         3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] -
  453|       |        three UTF-8 bytes
  454|       |
  455|       |        We expand the input word (16-bit) into two code units (32-bit), thus
  456|       |        we have room for four bytes. However, we need five distinct bit
  457|       |        layouts. Note that the last byte in cases #2 and #3 is the same.
  458|       |
  459|       |        We precompute byte 1 for case #1 and the common byte for cases #2 & #3
  460|       |        in register t2.
  461|       |
  462|       |        We precompute byte 1 for case #3 and -- **conditionally** -- precompute
  463|       |        either byte 1 for case #2 or byte 2 for case #3. Note that they
  464|       |        differ by exactly one bit.
  465|       |
  466|       |        Finally from these two code units we build proper UTF-8 sequence, taking
  467|       |        into account the case (i.e, the number of bytes to write).
  468|       |      */
  469|       |      /**
  470|       |       * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
  471|       |       * t2 => [0ccc|cccc] [10cc|cccc]
  472|       |       * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
  473|       |       */
  474|  65.3k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  475|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  476|  65.3k|      const __m256i t0 = _mm256_shuffle_epi8(in, dup_even);
  477|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  478|  65.3k|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  474|  65.3k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  479|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  480|  65.3k|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  474|  65.3k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  481|       |
  482|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  483|  65.3k|      const __m256i s0 = _mm256_srli_epi16(in, 4);
  484|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  485|  65.3k|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  474|  65.3k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  486|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  487|  65.3k|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  474|  65.3k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  488|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  489|  65.3k|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  474|  65.3k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  490|  65.3k|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  491|  65.3k|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  474|  65.3k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  492|  65.3k|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  493|  65.3k|#undef simdutf_vec
  494|       |
  495|       |      // 4. expand code units 16-bit => 32-bit
  496|  65.3k|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  497|  65.3k|      const __m256i out1 = _mm256_unpackhi_epi16(t2, s4);
  498|       |
  499|       |      // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
  500|  65.3k|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  501|  65.3k|                            (one_or_two_bytes_bitmask & 0xaaaaaaaa);
  502|       |      // Due to the wider registers, the following path is less likely to be
  503|       |      // useful.
  504|       |      /*if(mask == 0) {
  505|       |        // We only have three-byte code units. Use fast path.
  506|       |        const __m256i shuffle =
  507|       |      _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1,
  508|       |      2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); const __m256i utf8_0 =
  509|       |      _mm256_shuffle_epi8(out0, shuffle); const __m256i utf8_1 =
  510|       |      _mm256_shuffle_epi8(out1, shuffle);
  511|       |        _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0));
  512|       |        utf8_output += 12;
  513|       |        _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1));
  514|       |        utf8_output += 12;
  515|       |        _mm_storeu_si128((__m128i*)utf8_output,
  516|       |      _mm256_extractf128_si256(utf8_0,1)); utf8_output += 12;
  517|       |        _mm_storeu_si128((__m128i*)utf8_output,
  518|       |      _mm256_extractf128_si256(utf8_1,1)); utf8_output += 12; buf += 16;
  519|       |        continue;
  520|       |      }*/
  521|  65.3k|      const uint8_t mask0 = uint8_t(mask);
  522|  65.3k|      const uint8_t *row0 =
  523|  65.3k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  524|  65.3k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  525|  65.3k|      const __m128i utf8_0 =
  526|  65.3k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  527|       |
  528|  65.3k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  529|  65.3k|      const uint8_t *row1 =
  530|  65.3k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  531|  65.3k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  532|  65.3k|      const __m128i utf8_1 =
  533|  65.3k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  534|       |
  535|  65.3k|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  536|  65.3k|      const uint8_t *row2 =
  537|  65.3k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  538|  65.3k|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  539|  65.3k|      const __m128i utf8_2 =
  540|  65.3k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  541|       |
  542|  65.3k|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  543|  65.3k|      const uint8_t *row3 =
  544|  65.3k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  545|  65.3k|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  546|  65.3k|      const __m128i utf8_3 =
  547|  65.3k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  548|       |
  549|  65.3k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  550|  65.3k|      utf8_output += row0[0];
  551|  65.3k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  552|  65.3k|      utf8_output += row1[0];
  553|  65.3k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  554|  65.3k|      utf8_output += row2[0];
  555|  65.3k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  556|  65.3k|      utf8_output += row3[0];
  557|  65.3k|      buf += 16;
  558|       |      // surrogate pair(s) in a register
  559|  65.3k|    } else {
  560|       |      // Let us do a scalar fallback.
  561|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
  562|       |      // in the presence of surrogate pairs may require non-trivial tables.
  563|  7.36k|      size_t forward = 15;
  564|  7.36k|      size_t k = 0;
  565|  7.36k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (565:11): [True: 0, False: 7.36k]
  ------------------
  566|      0|        forward = size_t(end - buf - 1);
  567|      0|      }
  568|   108k|      for (; k < forward; k++) {
  ------------------
  |  Branch (568:14): [True: 101k, False: 7.22k]
  ------------------
  569|   101k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  570|   101k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (570:13): [True: 37.4k, False: 64.2k]
  ------------------
  571|  37.4k|          *utf8_output++ = char(word);
  572|  64.2k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (572:20): [True: 4.56k, False: 59.7k]
  ------------------
  573|  4.56k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  574|  4.56k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  575|  59.7k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (575:20): [True: 51.7k, False: 7.94k]
  ------------------
  576|  51.7k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  577|  51.7k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  578|  51.7k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  579|  51.7k|        } else {
  580|       |          // must be a surrogate pair
  581|  7.94k|          uint16_t diff = uint16_t(word - 0xD800);
  582|  7.94k|          uint16_t next_word =
  583|  7.94k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  584|  7.94k|          k++;
  585|  7.94k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  586|  7.94k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (586:15): [True: 146, False: 7.80k]
  ------------------
  587|    146|            return std::make_pair(
  588|    146|                result(error_code::SURROGATE, buf - start + k - 1),
  589|    146|                utf8_output);
  590|    146|          }
  591|  7.80k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  592|  7.80k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  593|  7.80k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  594|  7.80k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  595|  7.80k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  596|  7.80k|        }
  597|   101k|      }
  598|  7.22k|      buf += k;
  599|  7.22k|    }
  600|  72.6k|  } // while
  601|    410|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  602|    556|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_128avx2_convert_utf32_to_latin1EPKDimPc:
    3|    186|                             char *latin1_output) {
    4|    186|  const size_t rounded_len =
    5|    186|      len & ~0x1F; // Round down to nearest multiple of 32
    6|       |
    7|    186|  const __m256i high_bytes_mask = _mm256_set1_epi32(0xFFFFFF00);
    8|       |
    9|    794|  for (size_t i = 0; i < rounded_len; i += 4 * 8) {
  ------------------
  |  Branch (9:22): [True: 630, False: 164]
  ------------------
   10|    630|    __m256i a = _mm256_loadu_si256((__m256i *)(buf + 0 * 8));
   11|    630|    __m256i b = _mm256_loadu_si256((__m256i *)(buf + 1 * 8));
   12|    630|    __m256i c = _mm256_loadu_si256((__m256i *)(buf + 2 * 8));
   13|    630|    __m256i d = _mm256_loadu_si256((__m256i *)(buf + 3 * 8));
   14|       |
   15|    630|    const __m256i check_combined =
   16|    630|        _mm256_or_si256(_mm256_or_si256(a, b), _mm256_or_si256(c, d));
   17|       |
   18|    630|    if (!_mm256_testz_si256(check_combined, high_bytes_mask)) {
  ------------------
  |  Branch (18:9): [True: 22, False: 608]
  ------------------
   19|     22|      return std::make_pair(nullptr, latin1_output);
   20|     22|    }
   21|       |
   22|    608|    b = _mm256_slli_epi32(b, 1 * 8);
   23|    608|    c = _mm256_slli_epi32(c, 2 * 8);
   24|    608|    d = _mm256_slli_epi32(d, 3 * 8);
   25|       |
   26|       |    // clang-format off
   27|       |
   28|       |    // a  = [.. .. .. a7|.. .. .. a6|.. .. .. a5|.. .. .. a4||.. .. .. a3|.. .. .. a2|.. .. .. a1|.. .. .. a0]
   29|       |    // b  = [.. .. b7 ..|.. .. b6 ..|.. .. b5 ..|.. .. b4 ..||.. .. b3 ..|.. .. b2 ..|.. .. b1 ..|.. .. b0 ..]
   30|       |    // c  = [.. c7 .. ..|.. c6 .. ..|.. c5 .. ..|.. c4 .. ..||.. c3 .. ..|.. c2 .. ..|.. c1 .. ..|.. c0 .. ..]
   31|       |    // d  = [d7 .. .. ..|d6 .. .. ..|d5 .. .. ..|d4 .. .. ..||d3 .. .. ..|d2 .. .. ..|d1 .. .. ..|d0 .. .. ..]
   32|       |
   33|       |    // t0 = [d7 c7 b7 a7|d6 c6 b6 a6|d5 c5 b5 a5|d4 c4 b4 a4||d3 c3 b3 a3|d2 c2 b2 a2|d1 c1 b1 a1|d0 c0 b0 a0]
   34|    608|    const __m256i t0 =
   35|    608|        _mm256_or_si256(_mm256_or_si256(a, b), _mm256_or_si256(c, d));
   36|       |
   37|       |    // shuffle bytes within 128-bit lanes
   38|       |    // t1 = [d7 d6 d5 d4|c7 c6 c5 c4|b7 b6 b5 b4|a7 a6 a5 a4||d3 d2 d1 d0|c3 c2 c1 c0|b3 b2 b1 b0|a3 a2 a1 a0]
   39|    608|    const __m256i shuffle_bytes =
   40|    608|        _mm256_setr_epi8(0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15,
   41|    608|                         0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15);
   42|       |
   43|    608|    const __m256i t1 = _mm256_shuffle_epi8(t0, shuffle_bytes);
   44|       |
   45|       |    // reshuffle dwords
   46|       |    // t2 = [d7 d6 d5 d4|d3 d2 d1 d0|c7 c6 c5 c4|c3 c2 c1 c0||b7 b6 b5 b4|b3 b2 b1 b0|a7 a6 a5 a4|a3 a2 a1 a0]
   47|    608|    const __m256i shuffle_dwords = _mm256_setr_epi32(0, 4, 1, 5, 2, 6, 3, 7);
   48|    608|    const __m256i t2 = _mm256_permutevar8x32_epi32(t1, shuffle_dwords);
   49|       |// clang format on
   50|       |
   51|    608|    _mm256_storeu_si256((__m256i *)latin1_output, t2);
   52|       |
   53|    608|    latin1_output += 32;
   54|    608|    buf += 32;
   55|    608|  }
   56|       |
   57|    164|  return std::make_pair(buf, latin1_output);
   58|    186|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_140avx2_convert_utf32_to_latin1_with_errorsEPKDimPc:
   62|    420|                                         char *latin1_output) {
   63|    420|  const size_t rounded_len =
   64|    420|      len & ~0x1F; // Round down to nearest multiple of 32
   65|       |
   66|    420|  const char32_t *start = buf;
   67|       |
   68|    420|  const __m256i high_bytes_mask = _mm256_set1_epi32(0xFFFFFF00);
   69|       |
   70|  9.70k|  for (size_t i = 0; i < rounded_len; i += 4 * 8) {
  ------------------
  |  Branch (70:22): [True: 9.39k, False: 310]
  ------------------
   71|  9.39k|    __m256i a = _mm256_loadu_si256((__m256i *)(buf + 0 * 8));
   72|  9.39k|    __m256i b = _mm256_loadu_si256((__m256i *)(buf + 1 * 8));
   73|  9.39k|    __m256i c = _mm256_loadu_si256((__m256i *)(buf + 2 * 8));
   74|  9.39k|    __m256i d = _mm256_loadu_si256((__m256i *)(buf + 3 * 8));
   75|       |
   76|  9.39k|    const __m256i check_combined =
   77|  9.39k|        _mm256_or_si256(_mm256_or_si256(a, b), _mm256_or_si256(c, d));
   78|       |
   79|  9.39k|    if (!_mm256_testz_si256(check_combined, high_bytes_mask)) {
  ------------------
  |  Branch (79:9): [True: 110, False: 9.28k]
  ------------------
   80|       |      // Fallback to scalar code for handling errors
   81|    596|      for (int k = 0; k < 4 * 8; k++) {
  ------------------
  |  Branch (81:23): [True: 596, False: 0]
  ------------------
   82|    596|        char32_t codepoint = buf[k];
   83|    596|        if (codepoint <= 0xFF) {
  ------------------
  |  Branch (83:13): [True: 486, False: 110]
  ------------------
   84|    486|          *latin1_output++ = static_cast<char>(codepoint);
   85|    486|        } else {
   86|    110|          return std::make_pair(result(error_code::TOO_LARGE, buf - start + k),
   87|    110|                                latin1_output);
   88|    110|        }
   89|    596|      }
   90|    110|    }
   91|       |
   92|  9.28k|    b = _mm256_slli_epi32(b, 1 * 8);
   93|  9.28k|    c = _mm256_slli_epi32(c, 2 * 8);
   94|  9.28k|    d = _mm256_slli_epi32(d, 3 * 8);
   95|       |
   96|  9.28k|    const __m256i t0 =
   97|  9.28k|        _mm256_or_si256(_mm256_or_si256(a, b), _mm256_or_si256(c, d));
   98|       |
   99|  9.28k|    const __m256i shuffle_bytes =
  100|  9.28k|        _mm256_setr_epi8(0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15,
  101|  9.28k|                         0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15);
  102|       |
  103|  9.28k|    const __m256i t1 = _mm256_shuffle_epi8(t0, shuffle_bytes);
  104|       |
  105|  9.28k|    const __m256i shuffle_dwords = _mm256_setr_epi32(0, 4, 1, 5, 2, 6, 3, 7);
  106|  9.28k|    const __m256i t2 = _mm256_permutevar8x32_epi32(t1, shuffle_dwords);
  107|       |
  108|  9.28k|    _mm256_storeu_si256((__m256i *)latin1_output, t2);
  109|       |
  110|  9.28k|    latin1_output += 32;
  111|  9.28k|    buf += 32;
  112|  9.28k|  }
  113|       |
  114|    310|  return std::make_pair(result(error_code::SUCCESS, buf - start),
  115|    310|                        latin1_output);
  116|    420|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_127avx2_convert_utf32_to_utf16ILNS_10endiannessE0EEENSt3__14pairIPKDiPDsEES7_mS8_:
    4|    806|                            char16_t *utf16_output) {
    5|    806|  const char32_t *end = buf + len;
    6|       |
    7|    806|  const size_t safety_margin =
    8|    806|      12; // to avoid overruns, see issue
    9|       |          // https://github.com/simdutf/simdutf/issues/92
   10|    806|  __m256i forbidden_bytemask = _mm256_setzero_si256();
   11|       |
   12|    806|  const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000);
   13|    806|  const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800);
   14|    806|  const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800);
   15|       |
   16|   120k|  while (end - buf >= std::ptrdiff_t(8 + safety_margin)) {
  ------------------
  |  Branch (16:10): [True: 119k, False: 664]
  ------------------
   17|   119k|    const __m256i in = _mm256_loadu_si256((__m256i *)buf);
   18|       |
   19|   119k|    if (simdutf_likely(_mm256_testz_si256(in, v_ffff0000))) {
  ------------------
  |  |   90|   119k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (90:31): [True: 116k, False: 3.07k]
  |  |  ------------------
  ------------------
   20|       |      // no bits set above 16th bit <=> can pack to UTF16
   21|       |      // without surrogate pairs
   22|   116k|      forbidden_bytemask = _mm256_or_si256(
   23|   116k|          forbidden_bytemask,
   24|   116k|          _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800));
   25|       |
   26|   116k|      __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),
   27|   116k|                                              _mm256_extractf128_si256(in, 1));
   28|   116k|      if (big_endian) {
  ------------------
  |  Branch (28:11): [Folded, False: 116k]
  ------------------
   29|      0|        const __m128i swap =
   30|      0|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   31|      0|        utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
   32|      0|      }
   33|   116k|      _mm_storeu_si128((__m128i *)utf16_output, utf16_packed);
   34|   116k|      utf16_output += 8;
   35|   116k|      buf += 8;
   36|   116k|    } else {
   37|  3.07k|      size_t forward = 7;
   38|  3.07k|      size_t k = 0;
   39|  3.07k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (39:11): [True: 0, False: 3.07k]
  ------------------
   40|      0|        forward = size_t(end - buf - 1);
   41|      0|      }
   42|  23.9k|      for (; k < forward; k++) {
  ------------------
  |  Branch (42:14): [True: 21.0k, False: 2.93k]
  ------------------
   43|  21.0k|        uint32_t word = buf[k];
   44|  21.0k|        if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (44:13): [True: 7.93k, False: 13.0k]
  ------------------
   45|       |          // will not generate a surrogate pair
   46|  7.93k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (46:15): [True: 1.05k, False: 6.88k]
  |  Branch (46:33): [True: 14, False: 1.04k]
  ------------------
   47|     14|            return std::make_pair(nullptr, utf16_output);
   48|     14|          }
   49|  7.92k|          *utf16_output++ =
   50|  7.92k|              big_endian
  ------------------
  |  Branch (50:15): [Folded, False: 7.92k]
  ------------------
   51|  7.92k|                  ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8))
   52|  7.92k|                  : char16_t(word);
   53|  13.0k|        } else {
   54|       |          // will generate a surrogate pair
   55|  13.0k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (55:15): [True: 128, False: 12.9k]
  ------------------
   56|    128|            return std::make_pair(nullptr, utf16_output);
   57|    128|          }
   58|  12.9k|          word -= 0x10000;
   59|  12.9k|          uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
   60|  12.9k|          uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
   61|  12.9k|          if (big_endian) {
  ------------------
  |  Branch (61:15): [Folded, False: 12.9k]
  ------------------
   62|      0|            high_surrogate =
   63|      0|                uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
   64|      0|            low_surrogate =
   65|      0|                uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
   66|      0|          }
   67|  12.9k|          *utf16_output++ = char16_t(high_surrogate);
   68|  12.9k|          *utf16_output++ = char16_t(low_surrogate);
   69|  12.9k|        }
   70|  21.0k|      }
   71|  2.93k|      buf += k;
   72|  2.93k|    }
   73|   119k|  }
   74|       |
   75|       |  // check for invalid input
   76|    664|  if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (76:7): [True: 30, False: 634]
  ------------------
   77|     30|    return std::make_pair(nullptr, utf16_output);
   78|     30|  }
   79|       |
   80|    634|  return std::make_pair(buf, utf16_output);
   81|    664|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_127avx2_convert_utf32_to_utf16ILNS_10endiannessE1EEENSt3__14pairIPKDiPDsEES7_mS8_:
    4|    620|                            char16_t *utf16_output) {
    5|    620|  const char32_t *end = buf + len;
    6|       |
    7|    620|  const size_t safety_margin =
    8|    620|      12; // to avoid overruns, see issue
    9|       |          // https://github.com/simdutf/simdutf/issues/92
   10|    620|  __m256i forbidden_bytemask = _mm256_setzero_si256();
   11|       |
   12|    620|  const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000);
   13|    620|  const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800);
   14|    620|  const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800);
   15|       |
   16|  7.71k|  while (end - buf >= std::ptrdiff_t(8 + safety_margin)) {
  ------------------
  |  Branch (16:10): [True: 7.21k, False: 494]
  ------------------
   17|  7.21k|    const __m256i in = _mm256_loadu_si256((__m256i *)buf);
   18|       |
   19|  7.21k|    if (simdutf_likely(_mm256_testz_si256(in, v_ffff0000))) {
  ------------------
  |  |   90|  7.21k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (90:31): [True: 4.30k, False: 2.91k]
  |  |  ------------------
  ------------------
   20|       |      // no bits set above 16th bit <=> can pack to UTF16
   21|       |      // without surrogate pairs
   22|  4.30k|      forbidden_bytemask = _mm256_or_si256(
   23|  4.30k|          forbidden_bytemask,
   24|  4.30k|          _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800));
   25|       |
   26|  4.30k|      __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),
   27|  4.30k|                                              _mm256_extractf128_si256(in, 1));
   28|  4.30k|      if (big_endian) {
  ------------------
  |  Branch (28:11): [True: 4.30k, Folded]
  ------------------
   29|  4.30k|        const __m128i swap =
   30|  4.30k|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   31|  4.30k|        utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
   32|  4.30k|      }
   33|  4.30k|      _mm_storeu_si128((__m128i *)utf16_output, utf16_packed);
   34|  4.30k|      utf16_output += 8;
   35|  4.30k|      buf += 8;
   36|  4.30k|    } else {
   37|  2.91k|      size_t forward = 7;
   38|  2.91k|      size_t k = 0;
   39|  2.91k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (39:11): [True: 0, False: 2.91k]
  ------------------
   40|      0|        forward = size_t(end - buf - 1);
   41|      0|      }
   42|  22.7k|      for (; k < forward; k++) {
  ------------------
  |  Branch (42:14): [True: 19.9k, False: 2.79k]
  ------------------
   43|  19.9k|        uint32_t word = buf[k];
   44|  19.9k|        if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (44:13): [True: 1.98k, False: 17.9k]
  ------------------
   45|       |          // will not generate a surrogate pair
   46|  1.98k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (46:15): [True: 292, False: 1.69k]
  |  Branch (46:33): [True: 8, False: 284]
  ------------------
   47|      8|            return std::make_pair(nullptr, utf16_output);
   48|      8|          }
   49|  1.97k|          *utf16_output++ =
   50|  1.97k|              big_endian
  ------------------
  |  Branch (50:15): [True: 1.97k, Folded]
  ------------------
   51|  1.97k|                  ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8))
   52|  1.97k|                  : char16_t(word);
   53|  17.9k|        } else {
   54|       |          // will generate a surrogate pair
   55|  17.9k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (55:15): [True: 118, False: 17.8k]
  ------------------
   56|    118|            return std::make_pair(nullptr, utf16_output);
   57|    118|          }
   58|  17.8k|          word -= 0x10000;
   59|  17.8k|          uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
   60|  17.8k|          uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
   61|  17.8k|          if (big_endian) {
  ------------------
  |  Branch (61:15): [True: 17.8k, Folded]
  ------------------
   62|  17.8k|            high_surrogate =
   63|  17.8k|                uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
   64|  17.8k|            low_surrogate =
   65|  17.8k|                uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
   66|  17.8k|          }
   67|  17.8k|          *utf16_output++ = char16_t(high_surrogate);
   68|  17.8k|          *utf16_output++ = char16_t(low_surrogate);
   69|  17.8k|        }
   70|  19.9k|      }
   71|  2.79k|      buf += k;
   72|  2.79k|    }
   73|  7.21k|  }
   74|       |
   75|       |  // check for invalid input
   76|    494|  if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (76:7): [True: 42, False: 452]
  ------------------
   77|     42|    return std::make_pair(nullptr, utf16_output);
   78|     42|  }
   79|       |
   80|    452|  return std::make_pair(buf, utf16_output);
   81|    494|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_139avx2_convert_utf32_to_utf16_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPDsEEPKDimS7_:
   86|    748|                                        char16_t *utf16_output) {
   87|    748|  const char32_t *start = buf;
   88|    748|  const char32_t *end = buf + len;
   89|       |
   90|    748|  const size_t safety_margin =
   91|    748|      12; // to avoid overruns, see issue
   92|       |          // https://github.com/simdutf/simdutf/issues/92
   93|       |
   94|    748|  const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000);
   95|    748|  const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800);
   96|    748|  const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800);
   97|       |
   98|  8.21k|  while (end - buf >= std::ptrdiff_t(8 + safety_margin)) {
  ------------------
  |  Branch (98:10): [True: 7.70k, False: 516]
  ------------------
   99|  7.70k|    const __m256i in = _mm256_loadu_si256((__m256i *)buf);
  100|       |
  101|  7.70k|    if (simdutf_likely(_mm256_testz_si256(in, v_ffff0000))) {
  ------------------
  |  |   90|  7.70k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (90:31): [True: 5.87k, False: 1.82k]
  |  |  ------------------
  ------------------
  102|       |      // no bits set above 16th bit <=> can pack to UTF16 without surrogate
  103|       |      // pairs
  104|  5.87k|      const __m256i forbidden_bytemask =
  105|  5.87k|          _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800);
  106|  5.87k|      if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) !=
  ------------------
  |  Branch (106:11): [True: 24, False: 5.84k]
  ------------------
  107|  5.87k|          0x0) {
  108|     24|        return std::make_pair(result(error_code::SURROGATE, buf - start),
  109|     24|                              utf16_output);
  110|     24|      }
  111|       |
  112|  5.84k|      __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),
  113|  5.84k|                                              _mm256_extractf128_si256(in, 1));
  114|  5.84k|      if (big_endian) {
  ------------------
  |  Branch (114:11): [Folded, False: 5.84k]
  ------------------
  115|      0|        const __m128i swap =
  116|      0|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  117|      0|        utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
  118|      0|      }
  119|  5.84k|      _mm_storeu_si128((__m128i *)utf16_output, utf16_packed);
  120|  5.84k|      utf16_output += 8;
  121|  5.84k|      buf += 8;
  122|  5.84k|    } else {
  123|  1.82k|      size_t forward = 7;
  124|  1.82k|      size_t k = 0;
  125|  1.82k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (125:11): [True: 0, False: 1.82k]
  ------------------
  126|      0|        forward = size_t(end - buf - 1);
  127|      0|      }
  128|  13.5k|      for (; k < forward; k++) {
  ------------------
  |  Branch (128:14): [True: 11.9k, False: 1.62k]
  ------------------
  129|  11.9k|        uint32_t word = buf[k];
  130|  11.9k|        if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (130:13): [True: 6.69k, False: 5.25k]
  ------------------
  131|       |          // will not generate a surrogate pair
  132|  6.69k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (132:15): [True: 736, False: 5.95k]
  |  Branch (132:33): [True: 30, False: 706]
  ------------------
  133|     30|            return std::make_pair(
  134|     30|                result(error_code::SURROGATE, buf - start + k), utf16_output);
  135|     30|          }
  136|  6.66k|          *utf16_output++ =
  137|  6.66k|              big_endian
  ------------------
  |  Branch (137:15): [Folded, False: 6.66k]
  ------------------
  138|  6.66k|                  ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8))
  139|  6.66k|                  : char16_t(word);
  140|  6.66k|        } else {
  141|       |          // will generate a surrogate pair
  142|  5.25k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (142:15): [True: 178, False: 5.07k]
  ------------------
  143|    178|            return std::make_pair(
  144|    178|                result(error_code::TOO_LARGE, buf - start + k), utf16_output);
  145|    178|          }
  146|  5.07k|          word -= 0x10000;
  147|  5.07k|          uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
  148|  5.07k|          uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
  149|  5.07k|          if (big_endian) {
  ------------------
  |  Branch (149:15): [Folded, False: 5.07k]
  ------------------
  150|      0|            high_surrogate =
  151|      0|                uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
  152|      0|            low_surrogate =
  153|      0|                uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
  154|      0|          }
  155|  5.07k|          *utf16_output++ = char16_t(high_surrogate);
  156|  5.07k|          *utf16_output++ = char16_t(low_surrogate);
  157|  5.07k|        }
  158|  11.9k|      }
  159|  1.62k|      buf += k;
  160|  1.62k|    }
  161|  7.70k|  }
  162|       |
  163|    516|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf16_output);
  164|    748|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_139avx2_convert_utf32_to_utf16_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPDsEEPKDimS7_:
   86|    670|                                        char16_t *utf16_output) {
   87|    670|  const char32_t *start = buf;
   88|    670|  const char32_t *end = buf + len;
   89|       |
   90|    670|  const size_t safety_margin =
   91|    670|      12; // to avoid overruns, see issue
   92|       |          // https://github.com/simdutf/simdutf/issues/92
   93|       |
   94|    670|  const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000);
   95|    670|  const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800);
   96|    670|  const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800);
   97|       |
   98|  5.76k|  while (end - buf >= std::ptrdiff_t(8 + safety_margin)) {
  ------------------
  |  Branch (98:10): [True: 5.26k, False: 504]
  ------------------
   99|  5.26k|    const __m256i in = _mm256_loadu_si256((__m256i *)buf);
  100|       |
  101|  5.26k|    if (simdutf_likely(_mm256_testz_si256(in, v_ffff0000))) {
  ------------------
  |  |   90|  5.26k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (90:31): [True: 4.53k, False: 728]
  |  |  ------------------
  ------------------
  102|       |      // no bits set above 16th bit <=> can pack to UTF16 without surrogate
  103|       |      // pairs
  104|  4.53k|      const __m256i forbidden_bytemask =
  105|  4.53k|          _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800);
  106|  4.53k|      if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) !=
  ------------------
  |  Branch (106:11): [True: 4, False: 4.53k]
  ------------------
  107|  4.53k|          0x0) {
  108|      4|        return std::make_pair(result(error_code::SURROGATE, buf - start),
  109|      4|                              utf16_output);
  110|      4|      }
  111|       |
  112|  4.53k|      __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),
  113|  4.53k|                                              _mm256_extractf128_si256(in, 1));
  114|  4.53k|      if (big_endian) {
  ------------------
  |  Branch (114:11): [True: 4.53k, Folded]
  ------------------
  115|  4.53k|        const __m128i swap =
  116|  4.53k|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  117|  4.53k|        utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
  118|  4.53k|      }
  119|  4.53k|      _mm_storeu_si128((__m128i *)utf16_output, utf16_packed);
  120|  4.53k|      utf16_output += 8;
  121|  4.53k|      buf += 8;
  122|  4.53k|    } else {
  123|    728|      size_t forward = 7;
  124|    728|      size_t k = 0;
  125|    728|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (125:11): [True: 0, False: 728]
  ------------------
  126|      0|        forward = size_t(end - buf - 1);
  127|      0|      }
  128|  5.01k|      for (; k < forward; k++) {
  ------------------
  |  Branch (128:14): [True: 4.44k, False: 566]
  ------------------
  129|  4.44k|        uint32_t word = buf[k];
  130|  4.44k|        if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (130:13): [True: 2.23k, False: 2.21k]
  ------------------
  131|       |          // will not generate a surrogate pair
  132|  2.23k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (132:15): [True: 482, False: 1.74k]
  |  Branch (132:33): [True: 14, False: 468]
  ------------------
  133|     14|            return std::make_pair(
  134|     14|                result(error_code::SURROGATE, buf - start + k), utf16_output);
  135|     14|          }
  136|  2.21k|          *utf16_output++ =
  137|  2.21k|              big_endian
  ------------------
  |  Branch (137:15): [True: 2.21k, Folded]
  ------------------
  138|  2.21k|                  ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8))
  139|  2.21k|                  : char16_t(word);
  140|  2.21k|        } else {
  141|       |          // will generate a surrogate pair
  142|  2.21k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (142:15): [True: 148, False: 2.06k]
  ------------------
  143|    148|            return std::make_pair(
  144|    148|                result(error_code::TOO_LARGE, buf - start + k), utf16_output);
  145|    148|          }
  146|  2.06k|          word -= 0x10000;
  147|  2.06k|          uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
  148|  2.06k|          uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
  149|  2.06k|          if (big_endian) {
  ------------------
  |  Branch (149:15): [True: 2.06k, Folded]
  ------------------
  150|  2.06k|            high_surrogate =
  151|  2.06k|                uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
  152|  2.06k|            low_surrogate =
  153|  2.06k|                uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
  154|  2.06k|          }
  155|  2.06k|          *utf16_output++ = char16_t(high_surrogate);
  156|  2.06k|          *utf16_output++ = char16_t(low_surrogate);
  157|  2.06k|        }
  158|  4.44k|      }
  159|    566|      buf += k;
  160|    566|    }
  161|  5.26k|  }
  162|       |
  163|    504|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf16_output);
  164|    670|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_126avx2_convert_utf32_to_utf8EPKDimPc:
    2|    942|avx2_convert_utf32_to_utf8(const char32_t *buf, size_t len, char *utf8_output) {
    3|    942|  const char32_t *end = buf + len;
    4|    942|  const __m256i v_0000 = _mm256_setzero_si256();
    5|    942|  const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000);
    6|    942|  const __m256i v_ff80 = _mm256_set1_epi16((uint16_t)0xff80);
    7|    942|  const __m256i v_f800 = _mm256_set1_epi16((uint16_t)0xf800);
    8|    942|  const __m256i v_c080 = _mm256_set1_epi16((uint16_t)0xc080);
    9|    942|  const __m256i v_7fffffff = _mm256_set1_epi32((uint32_t)0x7fffffff);
   10|    942|  __m256i running_max = _mm256_setzero_si256();
   11|    942|  __m256i forbidden_bytemask = _mm256_setzero_si256();
   12|       |
   13|    942|  const size_t safety_margin =
   14|    942|      12; // to avoid overruns, see issue
   15|       |          // https://github.com/simdutf/simdutf/issues/92
   16|       |
   17|  42.5k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (17:10): [True: 41.8k, False: 702]
  ------------------
   18|  41.8k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
   19|  41.8k|    __m256i nextin = _mm256_loadu_si256((__m256i *)buf + 1);
   20|  41.8k|    running_max = _mm256_max_epu32(_mm256_max_epu32(in, running_max), nextin);
   21|       |
   22|       |    // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned
   23|       |    // saturation
   24|  41.8k|    __m256i in_16 = _mm256_packus_epi32(_mm256_and_si256(in, v_7fffffff),
   25|  41.8k|                                        _mm256_and_si256(nextin, v_7fffffff));
   26|  41.8k|    in_16 = _mm256_permute4x64_epi64(in_16, 0b11011000);
   27|       |
   28|       |    // Try to apply UTF-16 => UTF-8 routine on 256 bits
   29|       |    // (haswell/avx2_convert_utf16_to_utf8.cpp)
   30|       |
   31|  41.8k|    if (_mm256_testz_si256(in_16, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (31:9): [True: 34.5k, False: 7.33k]
  ------------------
   32|       |      // 1. pack the bytes
   33|  34.5k|      const __m128i utf8_packed = _mm_packus_epi16(
   34|  34.5k|          _mm256_castsi256_si128(in_16), _mm256_extractf128_si256(in_16, 1));
   35|       |      // 2. store (16 bytes)
   36|  34.5k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   37|       |      // 3. adjust pointers
   38|  34.5k|      buf += 16;
   39|  34.5k|      utf8_output += 16;
   40|  34.5k|      continue; // we are done for this round!
   41|  34.5k|    }
   42|       |    // no bits set above 7th bit
   43|  7.33k|    const __m256i one_byte_bytemask =
   44|  7.33k|        _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_ff80), v_0000);
   45|  7.33k|    const uint32_t one_byte_bitmask =
   46|  7.33k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
   47|       |
   48|       |    // no bits set above 11th bit
   49|  7.33k|    const __m256i one_or_two_bytes_bytemask =
   50|  7.33k|        _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_0000);
   51|  7.33k|    const uint32_t one_or_two_bytes_bitmask =
   52|  7.33k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
   53|  7.33k|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (53:9): [True: 4.20k, False: 3.12k]
  ------------------
   54|       |      // 1. prepare 2-byte values
   55|       |      // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
   56|       |      // expected output   : [110a|aaaa|10bb|bbbb] x 8
   57|  4.20k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
   58|  4.20k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
   59|       |
   60|       |      // t0 = [000a|aaaa|bbbb|bb00]
   61|  4.20k|      const __m256i t0 = _mm256_slli_epi16(in_16, 2);
   62|       |      // t1 = [000a|aaaa|0000|0000]
   63|  4.20k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
   64|       |      // t2 = [0000|0000|00bb|bbbb]
   65|  4.20k|      const __m256i t2 = _mm256_and_si256(in_16, v_003f);
   66|       |      // t3 = [000a|aaaa|00bb|bbbb]
   67|  4.20k|      const __m256i t3 = _mm256_or_si256(t1, t2);
   68|       |      // t4 = [110a|aaaa|10bb|bbbb]
   69|  4.20k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
   70|       |
   71|       |      // 2. merge ASCII and 2-byte codewords
   72|  4.20k|      const __m256i utf8_unpacked =
   73|  4.20k|          _mm256_blendv_epi8(t4, in_16, one_byte_bytemask);
   74|       |
   75|       |      // 3. prepare bitmask for 8-bit lookup
   76|  4.20k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
   77|  4.20k|      const uint32_t M1 = M0 >> 7;
   78|  4.20k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
   79|       |      // 4. pack the bytes
   80|       |
   81|  4.20k|      const uint8_t *row =
   82|  4.20k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
   83|  4.20k|      const uint8_t *row_2 =
   84|  4.20k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
   85|  4.20k|                                                                       16)][0];
   86|       |
   87|  4.20k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
   88|  4.20k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
   89|       |
   90|  4.20k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
   91|  4.20k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|  4.20k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|  4.20k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
   92|       |      // 5. store bytes
   93|  4.20k|      _mm_storeu_si128((__m128i *)utf8_output,
   94|  4.20k|                       _mm256_castsi256_si128(utf8_packed));
   95|  4.20k|      utf8_output += row[0];
   96|  4.20k|      _mm_storeu_si128((__m128i *)utf8_output,
   97|  4.20k|                       _mm256_extractf128_si256(utf8_packed, 1));
   98|  4.20k|      utf8_output += row_2[0];
   99|       |
  100|       |      // 6. adjust pointers
  101|  4.20k|      buf += 16;
  102|  4.20k|      continue;
  103|  4.20k|    }
  104|       |    // Must check for overflow in packing
  105|  3.12k|    const __m256i saturation_bytemask = _mm256_cmpeq_epi32(
  106|  3.12k|        _mm256_and_si256(_mm256_or_si256(in, nextin), v_ffff0000), v_0000);
  107|  3.12k|    const uint32_t saturation_bitmask =
  108|  3.12k|        static_cast<uint32_t>(_mm256_movemask_epi8(saturation_bytemask));
  109|  3.12k|    if (saturation_bitmask == 0xffffffff) {
  ------------------
  |  Branch (109:9): [True: 1.18k, False: 1.94k]
  ------------------
  110|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  111|  1.18k|      const __m256i v_d800 = _mm256_set1_epi16((uint16_t)0xd800);
  112|  1.18k|      forbidden_bytemask = _mm256_or_si256(
  113|  1.18k|          forbidden_bytemask,
  114|  1.18k|          _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_d800));
  115|       |
  116|  1.18k|      const __m256i dup_even = _mm256_setr_epi16(
  117|  1.18k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  118|  1.18k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
  119|       |
  120|       |      /* In this branch we handle three cases:
  121|       |        1. [0000|0000|0ccc|cccc] => [0ccc|cccc]                           -
  122|       |        single UFT-8 byte
  123|       |        2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc]              - two
  124|       |        UTF-8 bytes
  125|       |        3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] -
  126|       |        three UTF-8 bytes
  127|       |
  128|       |        We expand the input word (16-bit) into two code units (32-bit), thus
  129|       |        we have room for four bytes. However, we need five distinct bit
  130|       |        layouts. Note that the last byte in cases #2 and #3 is the same.
  131|       |
  132|       |        We precompute byte 1 for case #1 and the common byte for cases #2 & #3
  133|       |        in register t2.
  134|       |
  135|       |        We precompute byte 1 for case #3 and -- **conditionally** -- precompute
  136|       |        either byte 1 for case #2 or byte 2 for case #3. Note that they
  137|       |        differ by exactly one bit.
  138|       |
  139|       |        Finally from these two code units we build proper UTF-8 sequence, taking
  140|       |        into account the case (i.e, the number of bytes to write).
  141|       |      */
  142|       |      /**
  143|       |       * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
  144|       |       * t2 => [0ccc|cccc] [10cc|cccc]
  145|       |       * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
  146|       |       */
  147|  1.18k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  148|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  149|  1.18k|      const __m256i t0 = _mm256_shuffle_epi8(in_16, dup_even);
  150|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  151|  1.18k|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  147|  1.18k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  152|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  153|  1.18k|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  147|  1.18k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  154|       |
  155|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  156|  1.18k|      const __m256i s0 = _mm256_srli_epi16(in_16, 4);
  157|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  158|  1.18k|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  147|  1.18k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  159|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  160|  1.18k|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  147|  1.18k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  161|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  162|  1.18k|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  147|  1.18k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  163|  1.18k|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  164|  1.18k|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  147|  1.18k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  165|  1.18k|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  166|  1.18k|#undef simdutf_vec
  167|       |
  168|       |      // 4. expand code units 16-bit => 32-bit
  169|  1.18k|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  170|  1.18k|      const __m256i out1 = _mm256_unpackhi_epi16(t2, s4);
  171|       |
  172|       |      // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
  173|  1.18k|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  174|  1.18k|                            (one_or_two_bytes_bitmask & 0xaaaaaaaa);
  175|       |      // Due to the wider registers, the following path is less likely to be
  176|       |      // useful.
  177|       |      /*if(mask == 0) {
  178|       |        // We only have three-byte code units. Use fast path.
  179|       |        const __m256i shuffle =
  180|       |      _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1,
  181|       |      2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); const __m256i utf8_0 =
  182|       |      _mm256_shuffle_epi8(out0, shuffle); const __m256i utf8_1 =
  183|       |      _mm256_shuffle_epi8(out1, shuffle);
  184|       |        _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0));
  185|       |        utf8_output += 12;
  186|       |        _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1));
  187|       |        utf8_output += 12;
  188|       |        _mm_storeu_si128((__m128i*)utf8_output,
  189|       |      _mm256_extractf128_si256(utf8_0,1)); utf8_output += 12;
  190|       |        _mm_storeu_si128((__m128i*)utf8_output,
  191|       |      _mm256_extractf128_si256(utf8_1,1)); utf8_output += 12; buf += 16;
  192|       |        continue;
  193|       |      }*/
  194|  1.18k|      const uint8_t mask0 = uint8_t(mask);
  195|  1.18k|      const uint8_t *row0 =
  196|  1.18k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  197|  1.18k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  198|  1.18k|      const __m128i utf8_0 =
  199|  1.18k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  200|       |
  201|  1.18k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  202|  1.18k|      const uint8_t *row1 =
  203|  1.18k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  204|  1.18k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  205|  1.18k|      const __m128i utf8_1 =
  206|  1.18k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  207|       |
  208|  1.18k|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  209|  1.18k|      const uint8_t *row2 =
  210|  1.18k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  211|  1.18k|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  212|  1.18k|      const __m128i utf8_2 =
  213|  1.18k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  214|       |
  215|  1.18k|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  216|  1.18k|      const uint8_t *row3 =
  217|  1.18k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  218|  1.18k|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  219|  1.18k|      const __m128i utf8_3 =
  220|  1.18k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  221|       |
  222|  1.18k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  223|  1.18k|      utf8_output += row0[0];
  224|  1.18k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  225|  1.18k|      utf8_output += row1[0];
  226|  1.18k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  227|  1.18k|      utf8_output += row2[0];
  228|  1.18k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  229|  1.18k|      utf8_output += row3[0];
  230|  1.18k|      buf += 16;
  231|  1.94k|    } else {
  232|       |      // case: at least one 32-bit word is larger than 0xFFFF <=> it will
  233|       |      // produce four UTF-8 bytes. Let us do a scalar fallback. It may seem
  234|       |      // wasteful to use scalar code, but being efficient with SIMD may require
  235|       |      // large, non-trivial tables?
  236|  1.94k|      size_t forward = 15;
  237|  1.94k|      size_t k = 0;
  238|  1.94k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (238:11): [True: 0, False: 1.94k]
  ------------------
  239|      0|        forward = size_t(end - buf - 1);
  240|      0|      }
  241|  28.6k|      for (; k < forward; k++) {
  ------------------
  |  Branch (241:14): [True: 26.9k, False: 1.70k]
  ------------------
  242|  26.9k|        uint32_t word = buf[k];
  243|  26.9k|        if ((word & 0xFFFFFF80) == 0) { // 1-byte (ASCII)
  ------------------
  |  Branch (243:13): [True: 12.7k, False: 14.2k]
  ------------------
  244|  12.7k|          *utf8_output++ = char(word);
  245|  14.2k|        } else if ((word & 0xFFFFF800) == 0) { // 2-byte
  ------------------
  |  Branch (245:20): [True: 3.48k, False: 10.7k]
  ------------------
  246|  3.48k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  247|  3.48k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  248|  10.7k|        } else if ((word & 0xFFFF0000) == 0) { // 3-byte
  ------------------
  |  Branch (248:20): [True: 5.04k, False: 5.71k]
  ------------------
  249|  5.04k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (249:15): [True: 2.05k, False: 2.99k]
  |  Branch (249:33): [True: 24, False: 2.02k]
  ------------------
  250|     24|            return std::make_pair(nullptr, utf8_output);
  251|     24|          }
  252|  5.01k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  253|  5.01k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  254|  5.01k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  255|  5.71k|        } else { // 4-byte
  256|  5.71k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (256:15): [True: 216, False: 5.49k]
  ------------------
  257|    216|            return std::make_pair(nullptr, utf8_output);
  258|    216|          }
  259|  5.49k|          *utf8_output++ = char((word >> 18) | 0b11110000);
  260|  5.49k|          *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
  261|  5.49k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  262|  5.49k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  263|  5.49k|        }
  264|  26.9k|      }
  265|  1.70k|      buf += k;
  266|  1.70k|    }
  267|  3.12k|  } // while
  268|       |
  269|       |  // check for invalid input
  270|    702|  const __m256i v_10ffff = _mm256_set1_epi32((uint32_t)0x10ffff);
  271|    702|  if (static_cast<uint32_t>(_mm256_movemask_epi8(_mm256_cmpeq_epi32(
  ------------------
  |  Branch (271:7): [True: 30, False: 672]
  ------------------
  272|    702|          _mm256_max_epu32(running_max, v_10ffff), v_10ffff))) != 0xffffffff) {
  273|     30|    return std::make_pair(nullptr, utf8_output);
  274|     30|  }
  275|       |
  276|    672|  if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (276:7): [True: 56, False: 616]
  ------------------
  277|     56|    return std::make_pair(nullptr, utf8_output);
  278|     56|  }
  279|       |
  280|    616|  return std::make_pair(buf, utf8_output);
  281|    672|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_138avx2_convert_utf32_to_utf8_with_errorsEPKDimPc:
  285|    864|                                       char *utf8_output) {
  286|    864|  const char32_t *end = buf + len;
  287|    864|  const char32_t *start = buf;
  288|       |
  289|    864|  const __m256i v_0000 = _mm256_setzero_si256();
  290|    864|  const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000);
  291|    864|  const __m256i v_ff80 = _mm256_set1_epi16((uint16_t)0xff80);
  292|    864|  const __m256i v_f800 = _mm256_set1_epi16((uint16_t)0xf800);
  293|    864|  const __m256i v_c080 = _mm256_set1_epi16((uint16_t)0xc080);
  294|    864|  const __m256i v_7fffffff = _mm256_set1_epi32((uint32_t)0x7fffffff);
  295|    864|  const __m256i v_10ffff = _mm256_set1_epi32((uint32_t)0x10ffff);
  296|       |
  297|    864|  const size_t safety_margin =
  298|    864|      12; // to avoid overruns, see issue
  299|       |          // https://github.com/simdutf/simdutf/issues/92
  300|       |
  301|  13.4k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (301:10): [True: 12.8k, False: 636]
  ------------------
  302|  12.8k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
  303|  12.8k|    __m256i nextin = _mm256_loadu_si256((__m256i *)buf + 1);
  304|       |    // Check for too large input
  305|  12.8k|    const __m256i max_input =
  306|  12.8k|        _mm256_max_epu32(_mm256_max_epu32(in, nextin), v_10ffff);
  307|  12.8k|    if (static_cast<uint32_t>(_mm256_movemask_epi8(
  ------------------
  |  Branch (307:9): [True: 160, False: 12.6k]
  ------------------
  308|  12.8k|            _mm256_cmpeq_epi32(max_input, v_10ffff))) != 0xffffffff) {
  309|    160|      return std::make_pair(result(error_code::TOO_LARGE, buf - start),
  310|    160|                            utf8_output);
  311|    160|    }
  312|       |
  313|       |    // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned
  314|       |    // saturation
  315|  12.6k|    __m256i in_16 = _mm256_packus_epi32(_mm256_and_si256(in, v_7fffffff),
  316|  12.6k|                                        _mm256_and_si256(nextin, v_7fffffff));
  317|  12.6k|    in_16 = _mm256_permute4x64_epi64(in_16, 0b11011000);
  318|       |
  319|       |    // Try to apply UTF-16 => UTF-8 routine on 256 bits
  320|       |    // (haswell/avx2_convert_utf16_to_utf8.cpp)
  321|       |
  322|  12.6k|    if (_mm256_testz_si256(in_16, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (322:9): [True: 7.64k, False: 5.00k]
  ------------------
  323|       |      // 1. pack the bytes
  324|  7.64k|      const __m128i utf8_packed = _mm_packus_epi16(
  325|  7.64k|          _mm256_castsi256_si128(in_16), _mm256_extractf128_si256(in_16, 1));
  326|       |      // 2. store (16 bytes)
  327|  7.64k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  328|       |      // 3. adjust pointers
  329|  7.64k|      buf += 16;
  330|  7.64k|      utf8_output += 16;
  331|  7.64k|      continue; // we are done for this round!
  332|  7.64k|    }
  333|       |    // no bits set above 7th bit
  334|  5.00k|    const __m256i one_byte_bytemask =
  335|  5.00k|        _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_ff80), v_0000);
  336|  5.00k|    const uint32_t one_byte_bitmask =
  337|  5.00k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
  338|       |
  339|       |    // no bits set above 11th bit
  340|  5.00k|    const __m256i one_or_two_bytes_bytemask =
  341|  5.00k|        _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_0000);
  342|  5.00k|    const uint32_t one_or_two_bytes_bitmask =
  343|  5.00k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
  344|  5.00k|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (344:9): [True: 2.79k, False: 2.21k]
  ------------------
  345|       |      // 1. prepare 2-byte values
  346|       |      // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
  347|       |      // expected output   : [110a|aaaa|10bb|bbbb] x 8
  348|  2.79k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
  349|  2.79k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
  350|       |
  351|       |      // t0 = [000a|aaaa|bbbb|bb00]
  352|  2.79k|      const __m256i t0 = _mm256_slli_epi16(in_16, 2);
  353|       |      // t1 = [000a|aaaa|0000|0000]
  354|  2.79k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
  355|       |      // t2 = [0000|0000|00bb|bbbb]
  356|  2.79k|      const __m256i t2 = _mm256_and_si256(in_16, v_003f);
  357|       |      // t3 = [000a|aaaa|00bb|bbbb]
  358|  2.79k|      const __m256i t3 = _mm256_or_si256(t1, t2);
  359|       |      // t4 = [110a|aaaa|10bb|bbbb]
  360|  2.79k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
  361|       |
  362|       |      // 2. merge ASCII and 2-byte codewords
  363|  2.79k|      const __m256i utf8_unpacked =
  364|  2.79k|          _mm256_blendv_epi8(t4, in_16, one_byte_bytemask);
  365|       |
  366|       |      // 3. prepare bitmask for 8-bit lookup
  367|  2.79k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
  368|  2.79k|      const uint32_t M1 = M0 >> 7;
  369|  2.79k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
  370|       |      // 4. pack the bytes
  371|       |
  372|  2.79k|      const uint8_t *row =
  373|  2.79k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
  374|  2.79k|      const uint8_t *row_2 =
  375|  2.79k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
  376|  2.79k|                                                                       16)][0];
  377|       |
  378|  2.79k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  379|  2.79k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
  380|       |
  381|  2.79k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
  382|  2.79k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|  2.79k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|  2.79k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  383|       |      // 5. store bytes
  384|  2.79k|      _mm_storeu_si128((__m128i *)utf8_output,
  385|  2.79k|                       _mm256_castsi256_si128(utf8_packed));
  386|  2.79k|      utf8_output += row[0];
  387|  2.79k|      _mm_storeu_si128((__m128i *)utf8_output,
  388|  2.79k|                       _mm256_extractf128_si256(utf8_packed, 1));
  389|  2.79k|      utf8_output += row_2[0];
  390|       |
  391|       |      // 6. adjust pointers
  392|  2.79k|      buf += 16;
  393|  2.79k|      continue;
  394|  2.79k|    }
  395|       |    // Must check for overflow in packing
  396|  2.21k|    const __m256i saturation_bytemask = _mm256_cmpeq_epi32(
  397|  2.21k|        _mm256_and_si256(_mm256_or_si256(in, nextin), v_ffff0000), v_0000);
  398|  2.21k|    const uint32_t saturation_bitmask =
  399|  2.21k|        static_cast<uint32_t>(_mm256_movemask_epi8(saturation_bytemask));
  400|  2.21k|    if (saturation_bitmask == 0xffffffff) {
  ------------------
  |  Branch (400:9): [True: 800, False: 1.41k]
  ------------------
  401|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  402|       |
  403|       |      // Check for illegal surrogate code units
  404|    800|      const __m256i v_d800 = _mm256_set1_epi16((uint16_t)0xd800);
  405|    800|      const __m256i forbidden_bytemask =
  406|    800|          _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_d800);
  407|    800|      if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) !=
  ------------------
  |  Branch (407:11): [True: 54, False: 746]
  ------------------
  408|    800|          0x0) {
  409|     54|        return std::make_pair(result(error_code::SURROGATE, buf - start),
  410|     54|                              utf8_output);
  411|     54|      }
  412|       |
  413|    746|      const __m256i dup_even = _mm256_setr_epi16(
  414|    746|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  415|    746|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
  416|       |
  417|       |      /* In this branch we handle three cases:
  418|       |        1. [0000|0000|0ccc|cccc] => [0ccc|cccc]                           -
  419|       |        single UFT-8 byte
  420|       |        2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc]              - two
  421|       |        UTF-8 bytes
  422|       |        3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] -
  423|       |        three UTF-8 bytes
  424|       |
  425|       |        We expand the input word (16-bit) into two code units (32-bit), thus
  426|       |        we have room for four bytes. However, we need five distinct bit
  427|       |        layouts. Note that the last byte in cases #2 and #3 is the same.
  428|       |
  429|       |        We precompute byte 1 for case #1 and the common byte for cases #2 & #3
  430|       |        in register t2.
  431|       |
  432|       |        We precompute byte 1 for case #3 and -- **conditionally** -- precompute
  433|       |        either byte 1 for case #2 or byte 2 for case #3. Note that they
  434|       |        differ by exactly one bit.
  435|       |
  436|       |        Finally from these two code units we build proper UTF-8 sequence, taking
  437|       |        into account the case (i.e, the number of bytes to write).
  438|       |      */
  439|       |      /**
  440|       |       * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
  441|       |       * t2 => [0ccc|cccc] [10cc|cccc]
  442|       |       * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
  443|       |       */
  444|    746|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  445|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  446|    746|      const __m256i t0 = _mm256_shuffle_epi8(in_16, dup_even);
  447|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  448|    746|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  444|    746|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  449|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  450|    746|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  444|    746|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  451|       |
  452|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  453|    746|      const __m256i s0 = _mm256_srli_epi16(in_16, 4);
  454|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  455|    746|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  444|    746|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  456|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  457|    746|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  444|    746|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  458|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  459|    746|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  444|    746|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  460|    746|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  461|    746|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  444|    746|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  462|    746|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  463|    746|#undef simdutf_vec
  464|       |
  465|       |      // 4. expand code units 16-bit => 32-bit
  466|    746|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  467|    746|      const __m256i out1 = _mm256_unpackhi_epi16(t2, s4);
  468|       |
  469|       |      // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
  470|    746|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  471|    746|                            (one_or_two_bytes_bitmask & 0xaaaaaaaa);
  472|       |      // Due to the wider registers, the following path is less likely to be
  473|       |      // useful.
  474|       |      /*if(mask == 0) {
  475|       |        // We only have three-byte code units. Use fast path.
  476|       |        const __m256i shuffle =
  477|       |      _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1,
  478|       |      2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1); const __m256i utf8_0 =
  479|       |      _mm256_shuffle_epi8(out0, shuffle); const __m256i utf8_1 =
  480|       |      _mm256_shuffle_epi8(out1, shuffle);
  481|       |        _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0));
  482|       |        utf8_output += 12;
  483|       |        _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1));
  484|       |        utf8_output += 12;
  485|       |        _mm_storeu_si128((__m128i*)utf8_output,
  486|       |      _mm256_extractf128_si256(utf8_0,1)); utf8_output += 12;
  487|       |        _mm_storeu_si128((__m128i*)utf8_output,
  488|       |      _mm256_extractf128_si256(utf8_1,1)); utf8_output += 12; buf += 16;
  489|       |        continue;
  490|       |      }*/
  491|    746|      const uint8_t mask0 = uint8_t(mask);
  492|    746|      const uint8_t *row0 =
  493|    746|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  494|    746|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  495|    746|      const __m128i utf8_0 =
  496|    746|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  497|       |
  498|    746|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  499|    746|      const uint8_t *row1 =
  500|    746|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  501|    746|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  502|    746|      const __m128i utf8_1 =
  503|    746|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  504|       |
  505|    746|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  506|    746|      const uint8_t *row2 =
  507|    746|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  508|    746|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  509|    746|      const __m128i utf8_2 =
  510|    746|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  511|       |
  512|    746|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  513|    746|      const uint8_t *row3 =
  514|    746|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  515|    746|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  516|    746|      const __m128i utf8_3 =
  517|    746|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  518|       |
  519|    746|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  520|    746|      utf8_output += row0[0];
  521|    746|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  522|    746|      utf8_output += row1[0];
  523|    746|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  524|    746|      utf8_output += row2[0];
  525|    746|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  526|    746|      utf8_output += row3[0];
  527|    746|      buf += 16;
  528|  1.41k|    } else {
  529|       |      // case: at least one 32-bit word is larger than 0xFFFF <=> it will
  530|       |      // produce four UTF-8 bytes. Let us do a scalar fallback. It may seem
  531|       |      // wasteful to use scalar code, but being efficient with SIMD may require
  532|       |      // large, non-trivial tables?
  533|  1.41k|      size_t forward = 15;
  534|  1.41k|      size_t k = 0;
  535|  1.41k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (535:11): [True: 0, False: 1.41k]
  ------------------
  536|      0|        forward = size_t(end - buf - 1);
  537|      0|      }
  538|  22.4k|      for (; k < forward; k++) {
  ------------------
  |  Branch (538:14): [True: 21.0k, False: 1.40k]
  ------------------
  539|  21.0k|        uint32_t word = buf[k];
  540|  21.0k|        if ((word & 0xFFFFFF80) == 0) { // 1-byte (ASCII)
  ------------------
  |  Branch (540:13): [True: 7.85k, False: 13.1k]
  ------------------
  541|  7.85k|          *utf8_output++ = char(word);
  542|  13.1k|        } else if ((word & 0xFFFFF800) == 0) { // 2-byte
  ------------------
  |  Branch (542:20): [True: 2.04k, False: 11.1k]
  ------------------
  543|  2.04k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  544|  2.04k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  545|  11.1k|        } else if ((word & 0xFFFF0000) == 0) { // 3-byte
  ------------------
  |  Branch (545:20): [True: 3.60k, False: 7.55k]
  ------------------
  546|  3.60k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (546:15): [True: 1.37k, False: 2.22k]
  |  Branch (546:33): [True: 14, False: 1.36k]
  ------------------
  547|     14|            return std::make_pair(
  548|     14|                result(error_code::SURROGATE, buf - start + k), utf8_output);
  549|     14|          }
  550|  3.58k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  551|  3.58k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  552|  3.58k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  553|  7.55k|        } else { // 4-byte
  554|  7.55k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (554:15): [True: 0, False: 7.55k]
  ------------------
  555|      0|            return std::make_pair(
  556|      0|                result(error_code::TOO_LARGE, buf - start + k), utf8_output);
  557|      0|          }
  558|  7.55k|          *utf8_output++ = char((word >> 18) | 0b11110000);
  559|  7.55k|          *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
  560|  7.55k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  561|  7.55k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  562|  7.55k|        }
  563|  21.0k|      }
  564|  1.40k|      buf += k;
  565|  1.40k|    }
  566|  2.21k|  } // while
  567|       |
  568|    636|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  569|    864|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_129convert_masked_utf8_to_latin1EPKcmRPc:
    9|   302k|                                     char *&latin1_output) {
   10|       |  // we use an approach where we try to process up to 12 input bytes.
   11|       |  // Why 12 input bytes and not 16? Because we are concerned with the size of
   12|       |  // the lookup tables. Also 12 is nicely divisible by two and three.
   13|       |  //
   14|       |  //
   15|       |  // Optimization note: our main path below is load-latency dependent. Thus it
   16|       |  // is maybe beneficial to have fast paths that depend on branch prediction but
   17|       |  // have less latency. This results in more instructions but, potentially, also
   18|       |  // higher speeds.
   19|       |  //
   20|   302k|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   21|       |
   22|   302k|  const uint16_t input_utf8_end_of_code_point_mask =
   23|   302k|      utf8_end_of_code_point_mask &
   24|   302k|      0xfff; // we are only processing 12 bytes in case it is not all ASCII
   25|       |
   26|   302k|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (26:7): [True: 1.89k, False: 300k]
  ------------------
   27|       |    // We process the data in chunks of 12 bytes.
   28|  1.89k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(latin1_output), in);
   29|  1.89k|    latin1_output += 12; // We wrote 12 characters.
   30|  1.89k|    return 12;           // We consumed 1 bytes.
   31|  1.89k|  }
   32|       |  /// We do not have a fast path available, so we fallback.
   33|   300k|  const uint8_t idx =
   34|   300k|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
   35|   300k|  const uint8_t consumed =
   36|   300k|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
   37|       |  // this indicates an invalid input:
   38|   300k|  if (idx >= 64) {
  ------------------
  |  Branch (38:7): [True: 13.5k, False: 286k]
  ------------------
   39|  13.5k|    return consumed;
   40|  13.5k|  }
   41|       |  // Here we should have (idx < 64), if not, there is a bug in the validation or
   42|       |  // elsewhere. SIX (6) input code-code units this is a relatively easy scenario
   43|       |  // we process SIX (6) input code-code units. The max length in bytes of six
   44|       |  // code code units spanning between 1 and 2 bytes each is 12 bytes. On
   45|       |  // processors where pdep/pext is fast, we might be able to use a small lookup
   46|       |  // table.
   47|   286k|  const __m128i sh =
   48|   286k|      _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
   49|   286k|  const __m128i perm = _mm_shuffle_epi8(in, sh);
   50|   286k|  const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   51|   286k|  const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   52|   286k|  __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   53|   286k|  const __m128i latin1_packed = _mm_packus_epi16(composed, composed);
   54|       |  // writing 8 bytes even though we only care about the first 6 bytes.
   55|       |  // performance note: it would be faster to use _mm_storeu_si128, we should
   56|       |  // investigate.
   57|   286k|  _mm_storel_epi64((__m128i *)latin1_output, latin1_packed);
   58|   286k|  latin1_output += 6; // We wrote 6 bytes.
   59|   286k|  return consumed;
   60|   300k|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_128convert_masked_utf8_to_utf16ILNS_10endiannessE0EEEmPKcmRPDs:
   10|  2.79M|                                    char16_t *&utf16_output) {
   11|       |  // we use an approach where we try to process up to 12 input bytes.
   12|       |  // Why 12 input bytes and not 16? Because we are concerned with the size of
   13|       |  // the lookup tables. Also 12 is nicely divisible by two and three.
   14|       |  //
   15|       |  //
   16|       |  // Optimization note: our main path below is load-latency dependent. Thus it
   17|       |  // is maybe beneficial to have fast paths that depend on branch prediction but
   18|       |  // have less latency. This results in more instructions but, potentially, also
   19|       |  // higher speeds.
   20|       |  //
   21|       |  // We first try a few fast paths.
   22|  2.79M|  const __m128i swap =
   23|  2.79M|      _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   24|  2.79M|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   25|  2.79M|  const uint16_t input_utf8_end_of_code_point_mask =
   26|  2.79M|      utf8_end_of_code_point_mask & 0xfff;
   27|  2.79M|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (27:7): [True: 9.49k, False: 2.78M]
  ------------------
   28|       |    // We process the data in chunks of 12 bytes.
   29|  9.49k|    __m256i ascii = _mm256_cvtepu8_epi16(in);
   30|  9.49k|    if (big_endian) {
  ------------------
  |  Branch (30:9): [Folded, False: 9.49k]
  ------------------
   31|      0|      const __m256i swap256 = _mm256_setr_epi8(
   32|      0|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   33|      0|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   34|      0|      ascii = _mm256_shuffle_epi8(ascii, swap256);
   35|      0|    }
   36|  9.49k|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf16_output), ascii);
   37|  9.49k|    utf16_output += 12; // We wrote 12 16-bit characters.
   38|  9.49k|    return 12;          // We consumed 12 bytes.
   39|  9.49k|  }
   40|  2.78M|  if (((utf8_end_of_code_point_mask & 0xffff) == 0xaaaa)) {
  ------------------
  |  Branch (40:7): [True: 14.6k, False: 2.77M]
  ------------------
   41|       |    // We want to take 8 2-byte UTF-8 code units and turn them into 8 2-byte
   42|       |    // UTF-16 code units. There is probably a more efficient sequence, but the
   43|       |    // following might do.
   44|  14.6k|    const __m128i sh =
   45|  14.6k|        _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   46|  14.6k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   47|  14.6k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   48|  14.6k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   49|  14.6k|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   50|  14.6k|    if (big_endian)
  ------------------
  |  Branch (50:9): [Folded, False: 14.6k]
  ------------------
   51|      0|      composed = _mm_shuffle_epi8(composed, swap);
   52|  14.6k|    _mm_storeu_si128((__m128i *)utf16_output, composed);
   53|  14.6k|    utf16_output += 8; // We wrote 16 bytes, 8 code points.
   54|  14.6k|    return 16;
   55|  14.6k|  }
   56|  2.77M|  if (input_utf8_end_of_code_point_mask == 0x924) {
  ------------------
  |  Branch (56:7): [True: 4.14k, False: 2.76M]
  ------------------
   57|       |    // We want to take 4 3-byte UTF-8 code units and turn them into 4 2-byte
   58|       |    // UTF-16 code units. There is probably a more efficient sequence, but the
   59|       |    // following might do.
   60|  4.14k|    const __m128i sh =
   61|  4.14k|        _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
   62|  4.14k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   63|  4.14k|    const __m128i ascii =
   64|  4.14k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
   65|  4.14k|    const __m128i middlebyte =
   66|  4.14k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
   67|  4.14k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
   68|  4.14k|    const __m128i highbyte =
   69|  4.14k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
   70|  4.14k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
   71|  4.14k|    const __m128i composed =
   72|  4.14k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
   73|  4.14k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
   74|  4.14k|    if (big_endian)
  ------------------
  |  Branch (74:9): [Folded, False: 4.14k]
  ------------------
   75|      0|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
   76|  4.14k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
   77|  4.14k|    utf16_output += 4;
   78|  4.14k|    return 12;
   79|  4.14k|  }
   80|       |
   81|  2.76M|  const uint8_t idx = simdutf::tables::utf8_to_utf16::utf8bigindex
   82|  2.76M|      [input_utf8_end_of_code_point_mask][0];
   83|  2.76M|  const uint8_t consumed = simdutf::tables::utf8_to_utf16::utf8bigindex
   84|  2.76M|      [input_utf8_end_of_code_point_mask][1];
   85|  2.76M|  if (idx < 64) {
  ------------------
  |  Branch (85:7): [True: 2.33M, False: 429k]
  ------------------
   86|       |    // SIX (6) input code-code units
   87|       |    // this is a relatively easy scenario
   88|       |    // we process SIX (6) input code-code units. The max length in bytes of six
   89|       |    // code code units spanning between 1 and 2 bytes each is 12 bytes. On
   90|       |    // processors where pdep/pext is fast, we might be able to use a small
   91|       |    // lookup table.
   92|  2.33M|    const __m128i sh = _mm_loadu_si128(
   93|  2.33M|        (const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
   94|  2.33M|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   95|  2.33M|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   96|  2.33M|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   97|  2.33M|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   98|  2.33M|    if (big_endian)
  ------------------
  |  Branch (98:9): [Folded, False: 2.33M]
  ------------------
   99|      0|      composed = _mm_shuffle_epi8(composed, swap);
  100|  2.33M|    _mm_storeu_si128((__m128i *)utf16_output, composed);
  101|  2.33M|    utf16_output += 6; // We wrote 12 bytes, 6 code points. There is a potential
  102|       |                       // overflow of 4 bytes.
  103|  2.33M|  } else if (idx < 145) {
  ------------------
  |  Branch (103:14): [True: 374k, False: 55.1k]
  ------------------
  104|       |    // FOUR (4) input code-code units
  105|   374k|    const __m128i sh = _mm_loadu_si128(
  106|   374k|        (const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
  107|   374k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  108|   374k|    const __m128i ascii =
  109|   374k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
  110|   374k|    const __m128i middlebyte =
  111|   374k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
  112|   374k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  113|   374k|    const __m128i highbyte =
  114|   374k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
  115|   374k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
  116|   374k|    const __m128i composed =
  117|   374k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
  118|   374k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
  119|   374k|    if (big_endian)
  ------------------
  |  Branch (119:9): [Folded, False: 374k]
  ------------------
  120|      0|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
  121|   374k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
  122|   374k|    utf16_output += 4; // Here we overflow by 8 bytes.
  123|   374k|  } else if (idx < 209) {
  ------------------
  |  Branch (123:14): [True: 54.2k, False: 904]
  ------------------
  124|       |    // TWO (2) input code-code units
  125|       |    //////////////
  126|       |    // There might be garbage inputs where a leading byte mascarades as a
  127|       |    // four-byte leading byte (by being followed by 3 continuation byte), but is
  128|       |    // not greater than 0xf0. This could trigger a buffer overflow if we only
  129|       |    // counted leading bytes of the form 0xf0 as generating surrogate pairs,
  130|       |    // without further UTF-8 validation. Thus we must be careful to ensure that
  131|       |    // only leading bytes at least as large as 0xf0 generate surrogate pairs. We
  132|       |    // do as at the cost of an extra mask.
  133|       |    /////////////
  134|  54.2k|    const __m128i sh = _mm_loadu_si128(
  135|  54.2k|        (const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
  136|  54.2k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  137|  54.2k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
  138|  54.2k|    const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
  139|  54.2k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  140|  54.2k|    __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
  141|       |    // correct for spurious high bit
  142|  54.2k|    const __m128i correct =
  143|  54.2k|        _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
  144|  54.2k|    middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
  145|  54.2k|    const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4);
  146|       |    // We deliberately carry the leading four bits in highbyte if they are
  147|       |    // present, we remove them later when computing hightenbits.
  148|  54.2k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0xff000000));
  149|  54.2k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6);
  150|       |    // When we need to generate a surrogate pair (leading byte > 0xF0), then
  151|       |    // the corresponding 32-bit value in 'composed'  will be greater than
  152|       |    // > (0xff00000>>6) or > 0x3c00000. This can be used later to identify the
  153|       |    // location of the surrogate pairs.
  154|  54.2k|    const __m128i composed =
  155|  54.2k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
  156|  54.2k|                     _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
  157|  54.2k|    const __m128i composedminus =
  158|  54.2k|        _mm_sub_epi32(composed, _mm_set1_epi32(0x10000));
  159|  54.2k|    const __m128i lowtenbits =
  160|  54.2k|        _mm_and_si128(composedminus, _mm_set1_epi32(0x3ff));
  161|       |    // Notice the 0x3ff mask:
  162|  54.2k|    const __m128i hightenbits =
  163|  54.2k|        _mm_and_si128(_mm_srli_epi32(composedminus, 10), _mm_set1_epi32(0x3ff));
  164|  54.2k|    const __m128i lowtenbitsadd =
  165|  54.2k|        _mm_add_epi32(lowtenbits, _mm_set1_epi32(0xDC00));
  166|  54.2k|    const __m128i hightenbitsadd =
  167|  54.2k|        _mm_add_epi32(hightenbits, _mm_set1_epi32(0xD800));
  168|  54.2k|    const __m128i lowtenbitsaddshifted = _mm_slli_epi32(lowtenbitsadd, 16);
  169|  54.2k|    __m128i surrogates = _mm_or_si128(hightenbitsadd, lowtenbitsaddshifted);
  170|  54.2k|    uint32_t basic_buffer[4];
  171|  54.2k|    uint32_t basic_buffer_swap[4];
  172|  54.2k|    if (big_endian) {
  ------------------
  |  Branch (172:9): [Folded, False: 54.2k]
  ------------------
  173|      0|      _mm_storeu_si128((__m128i *)basic_buffer_swap,
  174|      0|                       _mm_shuffle_epi8(composed, swap));
  175|      0|      surrogates = _mm_shuffle_epi8(surrogates, swap);
  176|      0|    }
  177|  54.2k|    _mm_storeu_si128((__m128i *)basic_buffer, composed);
  178|  54.2k|    uint32_t surrogate_buffer[4];
  179|  54.2k|    _mm_storeu_si128((__m128i *)surrogate_buffer, surrogates);
  180|   217k|    for (size_t i = 0; i < 3; i++) {
  ------------------
  |  Branch (180:24): [True: 162k, False: 54.2k]
  ------------------
  181|   162k|      if (basic_buffer[i] > 0x3c00000) {
  ------------------
  |  Branch (181:11): [True: 45.2k, False: 117k]
  ------------------
  182|  45.2k|        utf16_output[0] = uint16_t(surrogate_buffer[i] & 0xffff);
  183|  45.2k|        utf16_output[1] = uint16_t(surrogate_buffer[i] >> 16);
  184|  45.2k|        utf16_output += 2;
  185|   117k|      } else {
  186|   117k|        utf16_output[0] = big_endian ? uint16_t(basic_buffer_swap[i])
  ------------------
  |  Branch (186:27): [Folded, False: 117k]
  ------------------
  187|   117k|                                     : uint16_t(basic_buffer[i]);
  188|   117k|        utf16_output++;
  189|   117k|      }
  190|   162k|    }
  191|  54.2k|  } else {
  192|       |    // here we know that there is an error but we do not handle errors
  193|    904|  }
  194|  2.76M|  return consumed;
  195|  2.77M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_128convert_masked_utf8_to_utf16ILNS_10endiannessE1EEEmPKcmRPDs:
   10|  4.08M|                                    char16_t *&utf16_output) {
   11|       |  // we use an approach where we try to process up to 12 input bytes.
   12|       |  // Why 12 input bytes and not 16? Because we are concerned with the size of
   13|       |  // the lookup tables. Also 12 is nicely divisible by two and three.
   14|       |  //
   15|       |  //
   16|       |  // Optimization note: our main path below is load-latency dependent. Thus it
   17|       |  // is maybe beneficial to have fast paths that depend on branch prediction but
   18|       |  // have less latency. This results in more instructions but, potentially, also
   19|       |  // higher speeds.
   20|       |  //
   21|       |  // We first try a few fast paths.
   22|  4.08M|  const __m128i swap =
   23|  4.08M|      _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   24|  4.08M|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   25|  4.08M|  const uint16_t input_utf8_end_of_code_point_mask =
   26|  4.08M|      utf8_end_of_code_point_mask & 0xfff;
   27|  4.08M|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (27:7): [True: 14.3k, False: 4.06M]
  ------------------
   28|       |    // We process the data in chunks of 12 bytes.
   29|  14.3k|    __m256i ascii = _mm256_cvtepu8_epi16(in);
   30|  14.3k|    if (big_endian) {
  ------------------
  |  Branch (30:9): [True: 14.3k, Folded]
  ------------------
   31|  14.3k|      const __m256i swap256 = _mm256_setr_epi8(
   32|  14.3k|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   33|  14.3k|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   34|  14.3k|      ascii = _mm256_shuffle_epi8(ascii, swap256);
   35|  14.3k|    }
   36|  14.3k|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf16_output), ascii);
   37|  14.3k|    utf16_output += 12; // We wrote 12 16-bit characters.
   38|  14.3k|    return 12;          // We consumed 12 bytes.
   39|  14.3k|  }
   40|  4.06M|  if (((utf8_end_of_code_point_mask & 0xffff) == 0xaaaa)) {
  ------------------
  |  Branch (40:7): [True: 29.8k, False: 4.03M]
  ------------------
   41|       |    // We want to take 8 2-byte UTF-8 code units and turn them into 8 2-byte
   42|       |    // UTF-16 code units. There is probably a more efficient sequence, but the
   43|       |    // following might do.
   44|  29.8k|    const __m128i sh =
   45|  29.8k|        _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   46|  29.8k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   47|  29.8k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   48|  29.8k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   49|  29.8k|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   50|  29.8k|    if (big_endian)
  ------------------
  |  Branch (50:9): [True: 29.8k, Folded]
  ------------------
   51|  29.8k|      composed = _mm_shuffle_epi8(composed, swap);
   52|  29.8k|    _mm_storeu_si128((__m128i *)utf16_output, composed);
   53|  29.8k|    utf16_output += 8; // We wrote 16 bytes, 8 code points.
   54|  29.8k|    return 16;
   55|  29.8k|  }
   56|  4.03M|  if (input_utf8_end_of_code_point_mask == 0x924) {
  ------------------
  |  Branch (56:7): [True: 13.9k, False: 4.02M]
  ------------------
   57|       |    // We want to take 4 3-byte UTF-8 code units and turn them into 4 2-byte
   58|       |    // UTF-16 code units. There is probably a more efficient sequence, but the
   59|       |    // following might do.
   60|  13.9k|    const __m128i sh =
   61|  13.9k|        _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
   62|  13.9k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   63|  13.9k|    const __m128i ascii =
   64|  13.9k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
   65|  13.9k|    const __m128i middlebyte =
   66|  13.9k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
   67|  13.9k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
   68|  13.9k|    const __m128i highbyte =
   69|  13.9k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
   70|  13.9k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
   71|  13.9k|    const __m128i composed =
   72|  13.9k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
   73|  13.9k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
   74|  13.9k|    if (big_endian)
  ------------------
  |  Branch (74:9): [True: 13.9k, Folded]
  ------------------
   75|  13.9k|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
   76|  13.9k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
   77|  13.9k|    utf16_output += 4;
   78|  13.9k|    return 12;
   79|  13.9k|  }
   80|       |
   81|  4.02M|  const uint8_t idx = simdutf::tables::utf8_to_utf16::utf8bigindex
   82|  4.02M|      [input_utf8_end_of_code_point_mask][0];
   83|  4.02M|  const uint8_t consumed = simdutf::tables::utf8_to_utf16::utf8bigindex
   84|  4.02M|      [input_utf8_end_of_code_point_mask][1];
   85|  4.02M|  if (idx < 64) {
  ------------------
  |  Branch (85:7): [True: 3.62M, False: 402k]
  ------------------
   86|       |    // SIX (6) input code-code units
   87|       |    // this is a relatively easy scenario
   88|       |    // we process SIX (6) input code-code units. The max length in bytes of six
   89|       |    // code code units spanning between 1 and 2 bytes each is 12 bytes. On
   90|       |    // processors where pdep/pext is fast, we might be able to use a small
   91|       |    // lookup table.
   92|  3.62M|    const __m128i sh = _mm_loadu_si128(
   93|  3.62M|        (const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
   94|  3.62M|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   95|  3.62M|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   96|  3.62M|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   97|  3.62M|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   98|  3.62M|    if (big_endian)
  ------------------
  |  Branch (98:9): [True: 3.62M, Folded]
  ------------------
   99|  3.62M|      composed = _mm_shuffle_epi8(composed, swap);
  100|  3.62M|    _mm_storeu_si128((__m128i *)utf16_output, composed);
  101|  3.62M|    utf16_output += 6; // We wrote 12 bytes, 6 code points. There is a potential
  102|       |                       // overflow of 4 bytes.
  103|  3.62M|  } else if (idx < 145) {
  ------------------
  |  Branch (103:14): [True: 297k, False: 104k]
  ------------------
  104|       |    // FOUR (4) input code-code units
  105|   297k|    const __m128i sh = _mm_loadu_si128(
  106|   297k|        (const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
  107|   297k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  108|   297k|    const __m128i ascii =
  109|   297k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
  110|   297k|    const __m128i middlebyte =
  111|   297k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
  112|   297k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  113|   297k|    const __m128i highbyte =
  114|   297k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
  115|   297k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
  116|   297k|    const __m128i composed =
  117|   297k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
  118|   297k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
  119|   297k|    if (big_endian)
  ------------------
  |  Branch (119:9): [True: 297k, Folded]
  ------------------
  120|   297k|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
  121|   297k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
  122|   297k|    utf16_output += 4; // Here we overflow by 8 bytes.
  123|   297k|  } else if (idx < 209) {
  ------------------
  |  Branch (123:14): [True: 103k, False: 1.67k]
  ------------------
  124|       |    // TWO (2) input code-code units
  125|       |    //////////////
  126|       |    // There might be garbage inputs where a leading byte mascarades as a
  127|       |    // four-byte leading byte (by being followed by 3 continuation byte), but is
  128|       |    // not greater than 0xf0. This could trigger a buffer overflow if we only
  129|       |    // counted leading bytes of the form 0xf0 as generating surrogate pairs,
  130|       |    // without further UTF-8 validation. Thus we must be careful to ensure that
  131|       |    // only leading bytes at least as large as 0xf0 generate surrogate pairs. We
  132|       |    // do as at the cost of an extra mask.
  133|       |    /////////////
  134|   103k|    const __m128i sh = _mm_loadu_si128(
  135|   103k|        (const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
  136|   103k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  137|   103k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
  138|   103k|    const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
  139|   103k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  140|   103k|    __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
  141|       |    // correct for spurious high bit
  142|   103k|    const __m128i correct =
  143|   103k|        _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
  144|   103k|    middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
  145|   103k|    const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4);
  146|       |    // We deliberately carry the leading four bits in highbyte if they are
  147|       |    // present, we remove them later when computing hightenbits.
  148|   103k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0xff000000));
  149|   103k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6);
  150|       |    // When we need to generate a surrogate pair (leading byte > 0xF0), then
  151|       |    // the corresponding 32-bit value in 'composed'  will be greater than
  152|       |    // > (0xff00000>>6) or > 0x3c00000. This can be used later to identify the
  153|       |    // location of the surrogate pairs.
  154|   103k|    const __m128i composed =
  155|   103k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
  156|   103k|                     _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
  157|   103k|    const __m128i composedminus =
  158|   103k|        _mm_sub_epi32(composed, _mm_set1_epi32(0x10000));
  159|   103k|    const __m128i lowtenbits =
  160|   103k|        _mm_and_si128(composedminus, _mm_set1_epi32(0x3ff));
  161|       |    // Notice the 0x3ff mask:
  162|   103k|    const __m128i hightenbits =
  163|   103k|        _mm_and_si128(_mm_srli_epi32(composedminus, 10), _mm_set1_epi32(0x3ff));
  164|   103k|    const __m128i lowtenbitsadd =
  165|   103k|        _mm_add_epi32(lowtenbits, _mm_set1_epi32(0xDC00));
  166|   103k|    const __m128i hightenbitsadd =
  167|   103k|        _mm_add_epi32(hightenbits, _mm_set1_epi32(0xD800));
  168|   103k|    const __m128i lowtenbitsaddshifted = _mm_slli_epi32(lowtenbitsadd, 16);
  169|   103k|    __m128i surrogates = _mm_or_si128(hightenbitsadd, lowtenbitsaddshifted);
  170|   103k|    uint32_t basic_buffer[4];
  171|   103k|    uint32_t basic_buffer_swap[4];
  172|   103k|    if (big_endian) {
  ------------------
  |  Branch (172:9): [True: 103k, Folded]
  ------------------
  173|   103k|      _mm_storeu_si128((__m128i *)basic_buffer_swap,
  174|   103k|                       _mm_shuffle_epi8(composed, swap));
  175|   103k|      surrogates = _mm_shuffle_epi8(surrogates, swap);
  176|   103k|    }
  177|   103k|    _mm_storeu_si128((__m128i *)basic_buffer, composed);
  178|   103k|    uint32_t surrogate_buffer[4];
  179|   103k|    _mm_storeu_si128((__m128i *)surrogate_buffer, surrogates);
  180|   412k|    for (size_t i = 0; i < 3; i++) {
  ------------------
  |  Branch (180:24): [True: 309k, False: 103k]
  ------------------
  181|   309k|      if (basic_buffer[i] > 0x3c00000) {
  ------------------
  |  Branch (181:11): [True: 86.2k, False: 222k]
  ------------------
  182|  86.2k|        utf16_output[0] = uint16_t(surrogate_buffer[i] & 0xffff);
  183|  86.2k|        utf16_output[1] = uint16_t(surrogate_buffer[i] >> 16);
  184|  86.2k|        utf16_output += 2;
  185|   222k|      } else {
  186|   222k|        utf16_output[0] = big_endian ? uint16_t(basic_buffer_swap[i])
  ------------------
  |  Branch (186:27): [True: 222k, Folded]
  ------------------
  187|   222k|                                     : uint16_t(basic_buffer[i]);
  188|   222k|        utf16_output++;
  189|   222k|      }
  190|   309k|    }
  191|   103k|  } else {
  192|       |    // here we know that there is an error but we do not handle errors
  193|  1.67k|  }
  194|  4.02M|  return consumed;
  195|  4.03M|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_128convert_masked_utf8_to_utf32EPKcmRPDi:
    9|  3.88M|                                    char32_t *&utf32_output) {
   10|       |  // we use an approach where we try to process up to 12 input bytes.
   11|       |  // Why 12 input bytes and not 16? Because we are concerned with the size of
   12|       |  // the lookup tables. Also 12 is nicely divisible by two and three.
   13|       |  //
   14|       |  //
   15|       |  // Optimization note: our main path below is load-latency dependent. Thus it
   16|       |  // is maybe beneficial to have fast paths that depend on branch prediction but
   17|       |  // have less latency. This results in more instructions but, potentially, also
   18|       |  // higher speeds.
   19|       |  //
   20|       |  // We first try a few fast paths.
   21|  3.88M|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   22|  3.88M|  const uint16_t input_utf8_end_of_code_point_mask =
   23|  3.88M|      utf8_end_of_code_point_mask & 0xfff;
   24|  3.88M|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (24:7): [True: 10.2k, False: 3.87M]
  ------------------
   25|       |    // We process the data in chunks of 12 bytes.
   26|  10.2k|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output),
   27|  10.2k|                        _mm256_cvtepu8_epi32(in));
   28|  10.2k|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output + 8),
   29|  10.2k|                        _mm256_cvtepu8_epi32(_mm_srli_si128(in, 8)));
   30|  10.2k|    utf32_output += 12; // We wrote 12 32-bit characters.
   31|  10.2k|    return 12;          // We consumed 12 bytes.
   32|  10.2k|  }
   33|  3.87M|  if (((utf8_end_of_code_point_mask & 0xffff) == 0xaaaa)) {
  ------------------
  |  Branch (33:7): [True: 23.8k, False: 3.85M]
  ------------------
   34|       |    // We want to take 8 2-byte UTF-8 code units and turn them into 8 4-byte
   35|       |    // UTF-32 code units. There is probably a more efficient sequence, but the
   36|       |    // following might do.
   37|  23.8k|    const __m128i sh =
   38|  23.8k|        _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   39|  23.8k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   40|  23.8k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   41|  23.8k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   42|  23.8k|    const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   43|  23.8k|    _mm256_storeu_si256((__m256i *)utf32_output,
   44|  23.8k|                        _mm256_cvtepu16_epi32(composed));
   45|  23.8k|    utf32_output += 8; // We wrote 16 bytes, 8 code points.
   46|  23.8k|    return 16;
   47|  23.8k|  }
   48|  3.85M|  if (input_utf8_end_of_code_point_mask == 0x924) {
  ------------------
  |  Branch (48:7): [True: 57.8k, False: 3.79M]
  ------------------
   49|       |    // We want to take 4 3-byte UTF-8 code units and turn them into 4 4-byte
   50|       |    // UTF-32 code units. There is probably a more efficient sequence, but the
   51|       |    // following might do.
   52|  57.8k|    const __m128i sh =
   53|  57.8k|        _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
   54|  57.8k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   55|  57.8k|    const __m128i ascii =
   56|  57.8k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
   57|  57.8k|    const __m128i middlebyte =
   58|  57.8k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
   59|  57.8k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
   60|  57.8k|    const __m128i highbyte =
   61|  57.8k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
   62|  57.8k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
   63|  57.8k|    const __m128i composed =
   64|  57.8k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
   65|  57.8k|    _mm_storeu_si128((__m128i *)utf32_output, composed);
   66|  57.8k|    utf32_output += 4;
   67|  57.8k|    return 12;
   68|  57.8k|  }
   69|       |  /// We do not have a fast path available, so we fallback.
   70|       |
   71|  3.79M|  const uint8_t idx =
   72|  3.79M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
   73|  3.79M|  const uint8_t consumed =
   74|  3.79M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
   75|  3.79M|  if (idx < 64) {
  ------------------
  |  Branch (75:7): [True: 3.47M, False: 326k]
  ------------------
   76|       |    // SIX (6) input code-code units
   77|       |    // this is a relatively easy scenario
   78|       |    // we process SIX (6) input code-code units. The max length in bytes of six
   79|       |    // code code units spanning between 1 and 2 bytes each is 12 bytes. On
   80|       |    // processors where pdep/pext is fast, we might be able to use a small
   81|       |    // lookup table.
   82|  3.47M|    const __m128i sh =
   83|  3.47M|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
   84|  3.47M|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   85|  3.47M|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   86|  3.47M|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   87|  3.47M|    const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   88|  3.47M|    _mm256_storeu_si256((__m256i *)utf32_output,
   89|  3.47M|                        _mm256_cvtepu16_epi32(composed));
   90|  3.47M|    utf32_output += 6; // We wrote 24 bytes, 6 code points. There is a potential
   91|       |    // overflow of 32 - 24 = 8 bytes.
   92|  3.47M|  } else if (idx < 145) {
  ------------------
  |  Branch (92:14): [True: 263k, False: 63.0k]
  ------------------
   93|       |    // FOUR (4) input code-code units
   94|   263k|    const __m128i sh =
   95|   263k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
   96|   263k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   97|   263k|    const __m128i ascii =
   98|   263k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
   99|   263k|    const __m128i middlebyte =
  100|   263k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
  101|   263k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  102|   263k|    const __m128i highbyte =
  103|   263k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
  104|   263k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
  105|   263k|    const __m128i composed =
  106|   263k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
  107|   263k|    _mm_storeu_si128((__m128i *)utf32_output, composed);
  108|   263k|    utf32_output += 4;
  109|   263k|  } else if (idx < 209) {
  ------------------
  |  Branch (109:14): [True: 61.8k, False: 1.23k]
  ------------------
  110|       |    // TWO (2) input code-code units
  111|  61.8k|    const __m128i sh =
  112|  61.8k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
  113|  61.8k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  114|  61.8k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
  115|  61.8k|    const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
  116|  61.8k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  117|  61.8k|    __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
  118|       |    // correct for spurious high bit
  119|  61.8k|    const __m128i correct =
  120|  61.8k|        _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
  121|  61.8k|    middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
  122|  61.8k|    const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4);
  123|  61.8k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0x07000000));
  124|  61.8k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6);
  125|  61.8k|    const __m128i composed =
  126|  61.8k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
  127|  61.8k|                     _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
  128|  61.8k|    _mm_storeu_si128((__m128i *)utf32_output, composed);
  129|  61.8k|    utf32_output +=
  130|  61.8k|        3; // We wrote 3 * 4 bytes, there is a potential overflow of 4 bytes.
  131|  61.8k|  } else {
  132|       |    // here we know that there is an error but we do not handle errors
  133|  1.23k|  }
  134|  3.79M|  return consumed;
  135|  3.85M|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1623utf16_gather_high_bytesILNS_10endiannessE0EEENS1_4simd5simd8IhEERKNS5_6simd16ItEESB_:
    3|  2.71M|                                       const simd16<uint16_t> &in1) {
    4|  2.71M|  if (big_endian) {
  ------------------
  |  Branch (4:7): [Folded, False: 2.71M]
  ------------------
    5|       |    // we want lower bytes
    6|      0|    const auto mask = simd16<uint16_t>(0x00ff);
    7|      0|    const auto t0 = in0 & mask;
    8|      0|    const auto t1 = in1 & mask;
    9|       |
   10|      0|    return simd16<uint16_t>::pack(t0, t1);
   11|  2.71M|  } else {
   12|  2.71M|    const auto t0 = in0.shr<8>();
   13|  2.71M|    const auto t1 = in1.shr<8>();
   14|       |
   15|  2.71M|    return simd16<uint16_t>::pack(t0, t1);
   16|  2.71M|  }
   17|  2.71M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1623utf16_gather_high_bytesILNS_10endiannessE1EEENS1_4simd5simd8IhEERKNS5_6simd16ItEESB_:
    3|  2.63M|                                       const simd16<uint16_t> &in1) {
    4|  2.63M|  if (big_endian) {
  ------------------
  |  Branch (4:7): [True: 2.63M, Folded]
  ------------------
    5|       |    // we want lower bytes
    6|  2.63M|    const auto mask = simd16<uint16_t>(0x00ff);
    7|  2.63M|    const auto t0 = in0 & mask;
    8|  2.63M|    const auto t1 = in1 & mask;
    9|       |
   10|  2.63M|    return simd16<uint16_t>::pack(t0, t1);
   11|  2.63M|  } else {
   12|      0|    const auto t0 = in0.shr<8>();
   13|      0|    const auto t1 = in1.shr<8>();
   14|       |
   15|      0|    return simd16<uint16_t>::pack(t0, t1);
   16|      0|  }
   17|  2.63M|}

_ZNK7simdutf7haswell14implementation13validate_utf8EPKcm:
  266|  4.20k|implementation::validate_utf8(const char *buf, size_t len) const noexcept {
  267|  4.20k|  return haswell::utf8_validation::generic_validate_utf8(buf, len);
  268|  4.20k|}
_ZNK7simdutf7haswell14implementation25validate_utf8_with_errorsEPKcm:
  273|  4.20k|    const char *buf, size_t len) const noexcept {
  274|  4.20k|  return haswell::utf8_validation::generic_validate_utf8_with_errors(buf, len);
  275|  4.20k|}
_ZNK7simdutf7haswell14implementation16validate_utf16leEPKDsm:
  312|  1.43k|                                 size_t len) const noexcept {
  313|  1.43k|  if (simdutf_unlikely(len == 0)) {
  ------------------
  |  |   93|  1.43k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (93:33): [True: 17, False: 1.41k]
  |  |  ------------------
  ------------------
  314|       |    // empty input is valid UTF-16. protect the implementation from
  315|       |    // handling nullptr
  316|     17|    return true;
  317|     17|  }
  318|  1.41k|  const auto res =
  319|  1.41k|      haswell::utf16::validate_utf16_with_errors<endianness::LITTLE>(buf, len);
  320|  1.41k|  if (res.is_err()) {
  ------------------
  |  Branch (320:7): [True: 96, False: 1.32k]
  ------------------
  321|     96|    return false;
  322|     96|  }
  323|       |
  324|  1.32k|  if (res.count == len) {
  ------------------
  |  Branch (324:7): [True: 0, False: 1.32k]
  ------------------
  325|      0|    return true;
  326|      0|  }
  327|       |
  328|  1.32k|  return scalar::utf16::validate<endianness::LITTLE>(buf + res.count,
  329|  1.32k|                                                     len - res.count);
  330|  1.32k|}
_ZNK7simdutf7haswell14implementation16validate_utf16beEPKDsm:
  336|  1.32k|                                 size_t len) const noexcept {
  337|  1.32k|  if (simdutf_unlikely(len == 0)) {
  ------------------
  |  |   93|  1.32k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (93:33): [True: 16, False: 1.30k]
  |  |  ------------------
  ------------------
  338|       |    // empty input is valid UTF-16. protect the implementation from
  339|       |    // handling nullptr
  340|     16|    return true;
  341|     16|  }
  342|  1.30k|  const auto res =
  343|  1.30k|      haswell::utf16::validate_utf16_with_errors<endianness::BIG>(buf, len);
  344|  1.30k|  if (res.is_err()) {
  ------------------
  |  Branch (344:7): [True: 98, False: 1.20k]
  ------------------
  345|     98|    return false;
  346|     98|  }
  347|       |
  348|  1.20k|  if (res.count == len) {
  ------------------
  |  Branch (348:7): [True: 0, False: 1.20k]
  ------------------
  349|      0|    return true;
  350|      0|  }
  351|       |
  352|  1.20k|  return scalar::utf16::validate<endianness::BIG>(buf + res.count,
  353|  1.20k|                                                  len - res.count);
  354|  1.20k|}
_ZNK7simdutf7haswell14implementation28validate_utf16le_with_errorsEPKDsm:
  357|  1.43k|    const char16_t *buf, size_t len) const noexcept {
  358|       |
  359|  1.43k|  const result res =
  360|  1.43k|      haswell::utf16::validate_utf16_with_errors<endianness::LITTLE>(buf, len);
  361|  1.43k|  if (res.count != len) {
  ------------------
  |  Branch (361:7): [True: 1.41k, False: 17]
  ------------------
  362|  1.41k|    const result scalar_res =
  363|  1.41k|        scalar::utf16::validate_with_errors<endianness::LITTLE>(
  364|  1.41k|            buf + res.count, len - res.count);
  365|  1.41k|    return result(scalar_res.error, res.count + scalar_res.count);
  366|  1.41k|  } else {
  367|     17|    return res;
  368|     17|  }
  369|  1.43k|}
_ZNK7simdutf7haswell14implementation28validate_utf16be_with_errorsEPKDsm:
  372|  1.32k|    const char16_t *buf, size_t len) const noexcept {
  373|  1.32k|  const result res =
  374|  1.32k|      haswell::utf16::validate_utf16_with_errors<endianness::BIG>(buf, len);
  375|  1.32k|  if (res.count != len) {
  ------------------
  |  Branch (375:7): [True: 1.30k, False: 16]
  ------------------
  376|  1.30k|    const result scalar_res =
  377|  1.30k|        scalar::utf16::validate_with_errors<endianness::BIG>(buf + res.count,
  378|  1.30k|                                                             len - res.count);
  379|  1.30k|    return result(scalar_res.error, res.count + scalar_res.count);
  380|  1.30k|  } else {
  381|     16|    return res;
  382|     16|  }
  383|  1.32k|}
_ZNK7simdutf7haswell14implementation14validate_utf32EPKDim:
  398|  2.64k|implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept {
  399|  2.64k|  return utf32::validate(buf, len);
  400|  2.64k|}
_ZNK7simdutf7haswell14implementation26validate_utf32_with_errorsEPKDim:
  405|  2.64k|    const char32_t *buf, size_t len) const noexcept {
  406|  2.64k|  return utf32::validate_with_errors(buf, len);
  407|  2.64k|}
_ZNK7simdutf7haswell14implementation22convert_latin1_to_utf8EPKcmPc:
  412|    560|    const char *buf, size_t len, char *utf8_output) const noexcept {
  413|    560|  std::pair<const char *, char *> ret =
  414|    560|      avx2_convert_latin1_to_utf8(buf, len, utf8_output);
  415|    560|  size_t converted_chars = ret.second - utf8_output;
  416|       |
  417|    560|  if (ret.first != buf + len) {
  ------------------
  |  Branch (417:7): [True: 556, False: 4]
  ------------------
  418|    556|    const size_t scalar_converted_chars = scalar::latin1_to_utf8::convert(
  419|    556|        ret.first, len - (ret.first - buf), ret.second);
  420|    556|    converted_chars += scalar_converted_chars;
  421|    556|  }
  422|       |
  423|    560|  return converted_chars;
  424|    560|}
_ZNK7simdutf7haswell14implementation25convert_latin1_to_utf16leEPKcmPDs:
  429|     76|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  430|     76|  std::pair<const char *, char16_t *> ret =
  431|     76|      avx2_convert_latin1_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
  432|     76|  if (ret.first == nullptr) {
  ------------------
  |  Branch (432:7): [True: 4, False: 72]
  ------------------
  433|      4|    return 0;
  434|      4|  }
  435|     72|  size_t converted_chars = ret.second - utf16_output;
  436|     72|  if (ret.first != buf + len) {
  ------------------
  |  Branch (436:7): [True: 58, False: 14]
  ------------------
  437|     58|    const size_t scalar_converted_chars =
  438|     58|        scalar::latin1_to_utf16::convert<endianness::LITTLE>(
  439|     58|            ret.first, len - (ret.first - buf), ret.second);
  440|     58|    if (scalar_converted_chars == 0) {
  ------------------
  |  Branch (440:9): [True: 0, False: 58]
  ------------------
  441|      0|      return 0;
  442|      0|    }
  443|     58|    converted_chars += scalar_converted_chars;
  444|     58|  }
  445|     72|  return converted_chars;
  446|     72|}
_ZNK7simdutf7haswell14implementation25convert_latin1_to_utf16beEPKcmPDs:
  449|    110|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  450|    110|  std::pair<const char *, char16_t *> ret =
  451|    110|      avx2_convert_latin1_to_utf16<endianness::BIG>(buf, len, utf16_output);
  452|    110|  if (ret.first == nullptr) {
  ------------------
  |  Branch (452:7): [True: 4, False: 106]
  ------------------
  453|      4|    return 0;
  454|      4|  }
  455|    106|  size_t converted_chars = ret.second - utf16_output;
  456|    106|  if (ret.first != buf + len) {
  ------------------
  |  Branch (456:7): [True: 88, False: 18]
  ------------------
  457|     88|    const size_t scalar_converted_chars =
  458|     88|        scalar::latin1_to_utf16::convert<endianness::BIG>(
  459|     88|            ret.first, len - (ret.first - buf), ret.second);
  460|     88|    if (scalar_converted_chars == 0) {
  ------------------
  |  Branch (460:9): [True: 0, False: 88]
  ------------------
  461|      0|      return 0;
  462|      0|    }
  463|     88|    converted_chars += scalar_converted_chars;
  464|     88|  }
  465|    106|  return converted_chars;
  466|    106|}
_ZNK7simdutf7haswell14implementation23convert_latin1_to_utf32EPKcmPDi:
  471|     96|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  472|     96|  std::pair<const char *, char32_t *> ret =
  473|     96|      avx2_convert_latin1_to_utf32(buf, len, utf32_output);
  474|     96|  if (ret.first == nullptr) {
  ------------------
  |  Branch (474:7): [True: 4, False: 92]
  ------------------
  475|      4|    return 0;
  476|      4|  }
  477|     92|  size_t converted_chars = ret.second - utf32_output;
  478|     92|  if (ret.first != buf + len) {
  ------------------
  |  Branch (478:7): [True: 58, False: 34]
  ------------------
  479|     58|    const size_t scalar_converted_chars = scalar::latin1_to_utf32::convert(
  480|     58|        ret.first, len - (ret.first - buf), ret.second);
  481|     58|    if (scalar_converted_chars == 0) {
  ------------------
  |  Branch (481:9): [True: 0, False: 58]
  ------------------
  482|      0|      return 0;
  483|      0|    }
  484|     58|    converted_chars += scalar_converted_chars;
  485|     58|  }
  486|     92|  return converted_chars;
  487|     92|}
_ZNK7simdutf7haswell14implementation22convert_utf8_to_latin1EPKcmPc:
  492|    710|    const char *buf, size_t len, char *latin1_output) const noexcept {
  493|    710|  utf8_to_latin1::validating_transcoder converter;
  494|    710|  return converter.convert(buf, len, latin1_output);
  495|    710|}
_ZNK7simdutf7haswell14implementation34convert_utf8_to_latin1_with_errorsEPKcmPc:
  498|    730|    const char *buf, size_t len, char *latin1_output) const noexcept {
  499|    730|  utf8_to_latin1::validating_transcoder converter;
  500|    730|  return converter.convert_with_errors(buf, len, latin1_output);
  501|    730|}
_ZNK7simdutf7haswell14implementation23convert_utf8_to_utf16leEPKcmPDs:
  511|    950|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  512|    950|  utf8_to_utf16::validating_transcoder converter;
  513|    950|  return converter.convert<endianness::LITTLE>(buf, len, utf16_output);
  514|    950|}
_ZNK7simdutf7haswell14implementation23convert_utf8_to_utf16beEPKcmPDs:
  517|  1.00k|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  518|  1.00k|  utf8_to_utf16::validating_transcoder converter;
  519|  1.00k|  return converter.convert<endianness::BIG>(buf, len, utf16_output);
  520|  1.00k|}
_ZNK7simdutf7haswell14implementation35convert_utf8_to_utf16le_with_errorsEPKcmPDs:
  523|    746|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  524|    746|  utf8_to_utf16::validating_transcoder converter;
  525|    746|  return converter.convert_with_errors<endianness::LITTLE>(buf, len,
  526|    746|                                                           utf16_output);
  527|    746|}
_ZNK7simdutf7haswell14implementation35convert_utf8_to_utf16be_with_errorsEPKcmPDs:
  530|    934|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  531|    934|  utf8_to_utf16::validating_transcoder converter;
  532|    934|  return converter.convert_with_errors<endianness::BIG>(buf, len, utf16_output);
  533|    934|}
_ZNK7simdutf7haswell14implementation29convert_valid_utf8_to_utf16leEPKcmPDs:
  536|    380|    const char *input, size_t size, char16_t *utf16_output) const noexcept {
  537|    380|  return utf8_to_utf16::convert_valid<endianness::LITTLE>(input, size,
  538|    380|                                                          utf16_output);
  539|    380|}
_ZNK7simdutf7haswell14implementation29convert_valid_utf8_to_utf16beEPKcmPDs:
  542|    518|    const char *input, size_t size, char16_t *utf16_output) const noexcept {
  543|    518|  return utf8_to_utf16::convert_valid<endianness::BIG>(input, size,
  544|    518|                                                       utf16_output);
  545|    518|}
_ZNK7simdutf7haswell14implementation21convert_utf8_to_utf32EPKcmPDi:
  550|    956|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  551|    956|  utf8_to_utf32::validating_transcoder converter;
  552|    956|  return converter.convert(buf, len, utf32_output);
  553|    956|}
_ZNK7simdutf7haswell14implementation33convert_utf8_to_utf32_with_errorsEPKcmPDi:
  556|    926|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  557|    926|  utf8_to_utf32::validating_transcoder converter;
  558|    926|  return converter.convert_with_errors(buf, len, utf32_output);
  559|    926|}
_ZNK7simdutf7haswell14implementation27convert_valid_utf8_to_utf32EPKcmPDi:
  562|    506|    const char *input, size_t size, char32_t *utf32_output) const noexcept {
  563|    506|  return utf8_to_utf32::convert_valid(input, size, utf32_output);
  564|    506|}
_ZNK7simdutf7haswell14implementation25convert_utf16le_to_latin1EPKDsmPc:
  569|    120|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  570|    120|  std::pair<const char16_t *, char *> ret =
  571|    120|      haswell::avx2_convert_utf16_to_latin1<endianness::LITTLE>(buf, len,
  572|    120|                                                                latin1_output);
  573|    120|  if (ret.first == nullptr) {
  ------------------
  |  Branch (573:7): [True: 30, False: 90]
  ------------------
  574|     30|    return 0;
  575|     30|  }
  576|     90|  size_t saved_bytes = ret.second - latin1_output;
  577|     90|  if (ret.first != buf + len) {
  ------------------
  |  Branch (577:7): [True: 76, False: 14]
  ------------------
  578|     76|    const size_t scalar_saved_bytes =
  579|     76|        scalar::utf16_to_latin1::convert<endianness::LITTLE>(
  580|     76|            ret.first, len - (ret.first - buf), ret.second);
  581|     76|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (581:9): [True: 46, False: 30]
  ------------------
  582|     46|      return 0;
  583|     46|    }
  584|     30|    saved_bytes += scalar_saved_bytes;
  585|     30|  }
  586|     44|  return saved_bytes;
  587|     90|}
_ZNK7simdutf7haswell14implementation25convert_utf16be_to_latin1EPKDsmPc:
  590|     98|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  591|     98|  std::pair<const char16_t *, char *> ret =
  592|     98|      haswell::avx2_convert_utf16_to_latin1<endianness::BIG>(buf, len,
  593|     98|                                                             latin1_output);
  594|     98|  if (ret.first == nullptr) {
  ------------------
  |  Branch (594:7): [True: 10, False: 88]
  ------------------
  595|     10|    return 0;
  596|     10|  }
  597|     88|  size_t saved_bytes = ret.second - latin1_output;
  598|     88|  if (ret.first != buf + len) {
  ------------------
  |  Branch (598:7): [True: 74, False: 14]
  ------------------
  599|     74|    const size_t scalar_saved_bytes =
  600|     74|        scalar::utf16_to_latin1::convert<endianness::BIG>(
  601|     74|            ret.first, len - (ret.first - buf), ret.second);
  602|     74|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (602:9): [True: 40, False: 34]
  ------------------
  603|     40|      return 0;
  604|     40|    }
  605|     34|    saved_bytes += scalar_saved_bytes;
  606|     34|  }
  607|     48|  return saved_bytes;
  608|     88|}
_ZNK7simdutf7haswell14implementation37convert_utf16le_to_latin1_with_errorsEPKDsmPc:
  612|    280|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  613|    280|  std::pair<result, char *> ret =
  614|    280|      avx2_convert_utf16_to_latin1_with_errors<endianness::LITTLE>(
  615|    280|          buf, len, latin1_output);
  616|    280|  if (ret.first.error) {
  ------------------
  |  Branch (616:7): [True: 110, False: 170]
  ------------------
  617|    110|    return ret.first;
  618|    110|  } // Can return directly since scalar fallback already found correct
  619|       |    // ret.first.count
  620|    170|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (620:7): [True: 134, False: 36]
  ------------------
  621|    134|    result scalar_res =
  622|    134|        scalar::utf16_to_latin1::convert_with_errors<endianness::LITTLE>(
  623|    134|            buf + ret.first.count, len - ret.first.count, ret.second);
  624|    134|    if (scalar_res.error) {
  ------------------
  |  Branch (624:9): [True: 64, False: 70]
  ------------------
  625|     64|      scalar_res.count += ret.first.count;
  626|     64|      return scalar_res;
  627|     70|    } else {
  628|     70|      ret.second += scalar_res.count;
  629|     70|    }
  630|    134|  }
  631|    106|  ret.first.count =
  632|    106|      ret.second -
  633|    106|      latin1_output; // Set count to the number of 8-bit code units written
  634|    106|  return ret.first;
  635|    170|}
_ZNK7simdutf7haswell14implementation37convert_utf16be_to_latin1_with_errorsEPKDsmPc:
  639|    248|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  640|    248|  std::pair<result, char *> ret =
  641|    248|      avx2_convert_utf16_to_latin1_with_errors<endianness::BIG>(buf, len,
  642|    248|                                                                latin1_output);
  643|    248|  if (ret.first.error) {
  ------------------
  |  Branch (643:7): [True: 102, False: 146]
  ------------------
  644|    102|    return ret.first;
  645|    102|  } // Can return directly since scalar fallback already found correct
  646|       |    // ret.first.count
  647|    146|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (647:7): [True: 116, False: 30]
  ------------------
  648|    116|    result scalar_res =
  649|    116|        scalar::utf16_to_latin1::convert_with_errors<endianness::BIG>(
  650|    116|            buf + ret.first.count, len - ret.first.count, ret.second);
  651|    116|    if (scalar_res.error) {
  ------------------
  |  Branch (651:9): [True: 60, False: 56]
  ------------------
  652|     60|      scalar_res.count += ret.first.count;
  653|     60|      return scalar_res;
  654|     60|    } else {
  655|     56|      ret.second += scalar_res.count;
  656|     56|    }
  657|    116|  }
  658|     86|  ret.first.count =
  659|     86|      ret.second -
  660|     86|      latin1_output; // Set count to the number of 8-bit code units written
  661|     86|  return ret.first;
  662|    146|}
_ZNK7simdutf7haswell14implementation23convert_utf16le_to_utf8EPKDsmPc:
  679|    874|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  680|    874|  std::pair<const char16_t *, char *> ret =
  681|    874|      haswell::avx2_convert_utf16_to_utf8<endianness::LITTLE>(buf, len,
  682|    874|                                                              utf8_output);
  683|    874|  if (ret.first == nullptr) {
  ------------------
  |  Branch (683:7): [True: 134, False: 740]
  ------------------
  684|    134|    return 0;
  685|    134|  }
  686|    740|  size_t saved_bytes = ret.second - utf8_output;
  687|    740|  if (ret.first != buf + len) {
  ------------------
  |  Branch (687:7): [True: 738, False: 2]
  ------------------
  688|    738|    const size_t scalar_saved_bytes =
  689|    738|        scalar::utf16_to_utf8::convert<endianness::LITTLE>(
  690|    738|            ret.first, len - (ret.first - buf), ret.second);
  691|    738|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (691:9): [True: 60, False: 678]
  ------------------
  692|     60|      return 0;
  693|     60|    }
  694|    678|    saved_bytes += scalar_saved_bytes;
  695|    678|  }
  696|    680|  return saved_bytes;
  697|    740|}
_ZNK7simdutf7haswell14implementation23convert_utf16be_to_utf8EPKDsmPc:
  700|    846|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  701|    846|  std::pair<const char16_t *, char *> ret =
  702|    846|      haswell::avx2_convert_utf16_to_utf8<endianness::BIG>(buf, len,
  703|    846|                                                           utf8_output);
  704|    846|  if (ret.first == nullptr) {
  ------------------
  |  Branch (704:7): [True: 120, False: 726]
  ------------------
  705|    120|    return 0;
  706|    120|  }
  707|    726|  size_t saved_bytes = ret.second - utf8_output;
  708|    726|  if (ret.first != buf + len) {
  ------------------
  |  Branch (708:7): [True: 724, False: 2]
  ------------------
  709|    724|    const size_t scalar_saved_bytes =
  710|    724|        scalar::utf16_to_utf8::convert<endianness::BIG>(
  711|    724|            ret.first, len - (ret.first - buf), ret.second);
  712|    724|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (712:9): [True: 66, False: 658]
  ------------------
  713|     66|      return 0;
  714|     66|    }
  715|    658|    saved_bytes += scalar_saved_bytes;
  716|    658|  }
  717|    660|  return saved_bytes;
  718|    726|}
_ZNK7simdutf7haswell14implementation35convert_utf16le_to_utf8_with_errorsEPKDsmPc:
  742|    686|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  743|    686|  return convert_utf16_to_utf8_with_details<endianness::LITTLE>(buf, len,
  744|    686|                                                                utf8_output);
  745|    686|}
_ZNK7simdutf7haswell14implementation35convert_utf16be_to_utf8_with_errorsEPKDsmPc:
  748|    556|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  749|    556|  return convert_utf16_to_utf8_with_details<endianness::BIG>(buf, len,
  750|    556|                                                             utf8_output);
  751|    556|}
_ZNK7simdutf7haswell14implementation29convert_valid_utf16le_to_utf8EPKDsmPc:
  754|    402|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  755|    402|  return convert_utf16le_to_utf8(buf, len, utf8_output);
  756|    402|}
_ZNK7simdutf7haswell14implementation29convert_valid_utf16be_to_utf8EPKDsmPc:
  759|    450|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  760|    450|  return convert_utf16be_to_utf8(buf, len, utf8_output);
  761|    450|}
_ZNK7simdutf7haswell14implementation21convert_utf32_to_utf8EPKDimPc:
  766|    942|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  767|    942|  std::pair<const char32_t *, char *> ret =
  768|    942|      avx2_convert_utf32_to_utf8(buf, len, utf8_output);
  769|    942|  if (ret.first == nullptr) {
  ------------------
  |  Branch (769:7): [True: 332, False: 610]
  ------------------
  770|    332|    return 0;
  771|    332|  }
  772|    610|  size_t saved_bytes = ret.second - utf8_output;
  773|    610|  if (ret.first != buf + len) {
  ------------------
  |  Branch (773:7): [True: 608, False: 2]
  ------------------
  774|    608|    SIMDUTF_VZEROUPPER();
  ------------------
  |  |   22|    608|#define SIMDUTF_VZEROUPPER() _mm256_zeroupper()
  ------------------
  775|    608|    const size_t scalar_saved_bytes = scalar::utf32_to_utf8::convert(
  776|    608|        ret.first, len - (ret.first - buf), ret.second);
  777|    608|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (777:9): [True: 262, False: 346]
  ------------------
  778|    262|      return 0;
  779|    262|    }
  780|    346|    saved_bytes += scalar_saved_bytes;
  781|    346|  }
  782|    348|  return saved_bytes;
  783|    610|}
_ZNK7simdutf7haswell14implementation23convert_utf32_to_latin1EPKDimPc:
  788|    186|    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
  789|    186|  std::pair<const char32_t *, char *> ret =
  790|    186|      avx2_convert_utf32_to_latin1(buf, len, latin1_output);
  791|    186|  if (ret.first == nullptr) {
  ------------------
  |  Branch (791:7): [True: 26, False: 160]
  ------------------
  792|     26|    return 0;
  793|     26|  }
  794|    160|  size_t saved_bytes = ret.second - latin1_output;
  795|    160|  if (ret.first != buf + len) {
  ------------------
  |  Branch (795:7): [True: 140, False: 20]
  ------------------
  796|    140|    const size_t scalar_saved_bytes = scalar::utf32_to_latin1::convert(
  797|    140|        ret.first, len - (ret.first - buf), ret.second);
  798|    140|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (798:9): [True: 120, False: 20]
  ------------------
  799|    120|      return 0;
  800|    120|    }
  801|     20|    saved_bytes += scalar_saved_bytes;
  802|     20|  }
  803|     40|  return saved_bytes;
  804|    160|}
_ZNK7simdutf7haswell14implementation35convert_utf32_to_latin1_with_errorsEPKDimPc:
  807|    420|    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
  808|       |  // ret.first.count is always the position in the buffer, not the number of
  809|       |  // code units written even if finished
  810|    420|  std::pair<result, char *> ret =
  811|    420|      avx2_convert_utf32_to_latin1_with_errors(buf, len, latin1_output);
  812|    420|  if (ret.first.count != len) {
  ------------------
  |  Branch (812:7): [True: 402, False: 18]
  ------------------
  813|    402|    result scalar_res = scalar::utf32_to_latin1::convert_with_errors(
  814|    402|        buf + ret.first.count, len - ret.first.count, ret.second);
  815|    402|    if (scalar_res.error) {
  ------------------
  |  Branch (815:9): [True: 376, False: 26]
  ------------------
  816|    376|      scalar_res.count += ret.first.count;
  817|    376|      return scalar_res;
  818|    376|    } else {
  819|     26|      ret.second += scalar_res.count;
  820|     26|    }
  821|    402|  }
  822|     44|  ret.first.count =
  823|     44|      ret.second -
  824|     44|      latin1_output; // Set count to the number of 8-bit code units written
  825|     44|  return ret.first;
  826|    420|}
_ZNK7simdutf7haswell14implementation33convert_utf32_to_utf8_with_errorsEPKDimPc:
  836|    864|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  837|       |  // ret.first.count is always the position in the buffer, not the number of
  838|       |  // code units written even if finished
  839|    864|  std::pair<result, char *> ret =
  840|    864|      haswell::avx2_convert_utf32_to_utf8_with_errors(buf, len, utf8_output);
  841|    864|  if (ret.first.count != len) {
  ------------------
  |  Branch (841:7): [True: 860, False: 4]
  ------------------
  842|    860|    result scalar_res = scalar::utf32_to_utf8::convert_with_errors(
  843|    860|        buf + ret.first.count, len - ret.first.count, ret.second);
  844|    860|    if (scalar_res.error) {
  ------------------
  |  Branch (844:9): [True: 656, False: 204]
  ------------------
  845|    656|      scalar_res.count += ret.first.count;
  846|    656|      return scalar_res;
  847|    656|    } else {
  848|    204|      ret.second += scalar_res.count;
  849|    204|    }
  850|    860|  }
  851|    208|  ret.first.count =
  852|    208|      ret.second -
  853|    208|      utf8_output; // Set count to the number of 8-bit code units written
  854|    208|  return ret.first;
  855|    864|}
_ZNK7simdutf7haswell14implementation24convert_utf16le_to_utf32EPKDsmPDi:
  860|    430|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  861|    430|  std::pair<const char16_t *, char32_t *> ret =
  862|    430|      haswell::avx2_convert_utf16_to_utf32<endianness::LITTLE>(buf, len,
  863|    430|                                                               utf32_output);
  864|    430|  if (ret.first == nullptr) {
  ------------------
  |  Branch (864:7): [True: 100, False: 330]
  ------------------
  865|    100|    return 0;
  866|    100|  }
  867|    330|  size_t saved_bytes = ret.second - utf32_output;
  868|    330|  if (ret.first != buf + len) {
  ------------------
  |  Branch (868:7): [True: 292, False: 38]
  ------------------
  869|    292|    const size_t scalar_saved_bytes =
  870|    292|        scalar::utf16_to_utf32::convert<endianness::LITTLE>(
  871|    292|            ret.first, len - (ret.first - buf), ret.second);
  872|    292|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (872:9): [True: 88, False: 204]
  ------------------
  873|     88|      return 0;
  874|     88|    }
  875|    204|    saved_bytes += scalar_saved_bytes;
  876|    204|  }
  877|    242|  return saved_bytes;
  878|    330|}
_ZNK7simdutf7haswell14implementation24convert_utf16be_to_utf32EPKDsmPDi:
  881|    458|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  882|    458|  std::pair<const char16_t *, char32_t *> ret =
  883|    458|      haswell::avx2_convert_utf16_to_utf32<endianness::BIG>(buf, len,
  884|    458|                                                            utf32_output);
  885|    458|  if (ret.first == nullptr) {
  ------------------
  |  Branch (885:7): [True: 88, False: 370]
  ------------------
  886|     88|    return 0;
  887|     88|  }
  888|    370|  size_t saved_bytes = ret.second - utf32_output;
  889|    370|  if (ret.first != buf + len) {
  ------------------
  |  Branch (889:7): [True: 326, False: 44]
  ------------------
  890|    326|    const size_t scalar_saved_bytes =
  891|    326|        scalar::utf16_to_utf32::convert<endianness::BIG>(
  892|    326|            ret.first, len - (ret.first - buf), ret.second);
  893|    326|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (893:9): [True: 88, False: 238]
  ------------------
  894|     88|      return 0;
  895|     88|    }
  896|    238|    saved_bytes += scalar_saved_bytes;
  897|    238|  }
  898|    282|  return saved_bytes;
  899|    370|}
_ZNK7simdutf7haswell14implementation36convert_utf16le_to_utf32_with_errorsEPKDsmPDi:
  902|    442|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  903|       |  // ret.first.count is always the position in the buffer, not the number of
  904|       |  // code units written even if finished
  905|    442|  std::pair<result, char32_t *> ret =
  906|    442|      haswell::avx2_convert_utf16_to_utf32_with_errors<endianness::LITTLE>(
  907|    442|          buf, len, utf32_output);
  908|    442|  if (ret.first.error) {
  ------------------
  |  Branch (908:7): [True: 136, False: 306]
  ------------------
  909|    136|    return ret.first;
  910|    136|  } // Can return directly since scalar fallback already found correct
  911|       |    // ret.first.count
  912|    306|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (912:7): [True: 270, False: 36]
  ------------------
  913|    270|    result scalar_res =
  914|    270|        scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
  915|    270|            buf + ret.first.count, len - ret.first.count, ret.second);
  916|    270|    if (scalar_res.error) {
  ------------------
  |  Branch (916:9): [True: 104, False: 166]
  ------------------
  917|    104|      scalar_res.count += ret.first.count;
  918|    104|      return scalar_res;
  919|    166|    } else {
  920|    166|      ret.second += scalar_res.count;
  921|    166|    }
  922|    270|  }
  923|    202|  ret.first.count =
  924|    202|      ret.second -
  925|    202|      utf32_output; // Set count to the number of 8-bit code units written
  926|    202|  return ret.first;
  927|    306|}
_ZNK7simdutf7haswell14implementation36convert_utf16be_to_utf32_with_errorsEPKDsmPDi:
  930|    360|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  931|       |  // ret.first.count is always the position in the buffer, not the number of
  932|       |  // code units written even if finished
  933|    360|  std::pair<result, char32_t *> ret =
  934|    360|      haswell::avx2_convert_utf16_to_utf32_with_errors<endianness::BIG>(
  935|    360|          buf, len, utf32_output);
  936|    360|  if (ret.first.error) {
  ------------------
  |  Branch (936:7): [True: 92, False: 268]
  ------------------
  937|     92|    return ret.first;
  938|     92|  } // Can return directly since scalar fallback already found correct
  939|       |    // ret.first.count
  940|    268|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (940:7): [True: 242, False: 26]
  ------------------
  941|    242|    result scalar_res =
  942|    242|        scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
  943|    242|            buf + ret.first.count, len - ret.first.count, ret.second);
  944|    242|    if (scalar_res.error) {
  ------------------
  |  Branch (944:9): [True: 108, False: 134]
  ------------------
  945|    108|      scalar_res.count += ret.first.count;
  946|    108|      return scalar_res;
  947|    134|    } else {
  948|    134|      ret.second += scalar_res.count;
  949|    134|    }
  950|    242|  }
  951|    160|  ret.first.count =
  952|    160|      ret.second -
  953|    160|      utf32_output; // Set count to the number of 8-bit code units written
  954|    160|  return ret.first;
  955|    268|}
_ZNK7simdutf7haswell14implementation27convert_valid_utf32_to_utf8EPKDimPc:
  960|    216|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  961|    216|  return convert_utf32_to_utf8(buf, len, utf8_output);
  962|    216|}
_ZNK7simdutf7haswell14implementation24convert_utf32_to_utf16leEPKDimPDs:
  967|    806|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  968|    806|  std::pair<const char32_t *, char16_t *> ret =
  969|    806|      avx2_convert_utf32_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
  970|    806|  if (ret.first == nullptr) {
  ------------------
  |  Branch (970:7): [True: 178, False: 628]
  ------------------
  971|    178|    return 0;
  972|    178|  }
  973|    628|  size_t saved_bytes = ret.second - utf16_output;
  974|    628|  if (ret.first != buf + len) {
  ------------------
  |  Branch (974:7): [True: 624, False: 4]
  ------------------
  975|    624|    const size_t scalar_saved_bytes =
  976|    624|        scalar::utf32_to_utf16::convert<endianness::LITTLE>(
  977|    624|            ret.first, len - (ret.first - buf), ret.second);
  978|    624|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (978:9): [True: 188, False: 436]
  ------------------
  979|    188|      return 0;
  980|    188|    }
  981|    436|    saved_bytes += scalar_saved_bytes;
  982|    436|  }
  983|    440|  return saved_bytes;
  984|    628|}
_ZNK7simdutf7haswell14implementation24convert_utf32_to_utf16beEPKDimPDs:
  987|    620|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  988|    620|  std::pair<const char32_t *, char16_t *> ret =
  989|    620|      avx2_convert_utf32_to_utf16<endianness::BIG>(buf, len, utf16_output);
  990|    620|  if (ret.first == nullptr) {
  ------------------
  |  Branch (990:7): [True: 172, False: 448]
  ------------------
  991|    172|    return 0;
  992|    172|  }
  993|    448|  size_t saved_bytes = ret.second - utf16_output;
  994|    448|  if (ret.first != buf + len) {
  ------------------
  |  Branch (994:7): [True: 446, False: 2]
  ------------------
  995|    446|    const size_t scalar_saved_bytes =
  996|    446|        scalar::utf32_to_utf16::convert<endianness::BIG>(
  997|    446|            ret.first, len - (ret.first - buf), ret.second);
  998|    446|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (998:9): [True: 170, False: 276]
  ------------------
  999|    170|      return 0;
 1000|    170|    }
 1001|    276|    saved_bytes += scalar_saved_bytes;
 1002|    276|  }
 1003|    278|  return saved_bytes;
 1004|    448|}
_ZNK7simdutf7haswell14implementation36convert_utf32_to_utf16le_with_errorsEPKDimPDs:
 1007|    748|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1008|       |  // ret.first.count is always the position in the buffer, not the number of
 1009|       |  // code units written even if finished
 1010|    748|  std::pair<result, char16_t *> ret =
 1011|    748|      haswell::avx2_convert_utf32_to_utf16_with_errors<endianness::LITTLE>(
 1012|    748|          buf, len, utf16_output);
 1013|    748|  if (ret.first.count != len) {
  ------------------
  |  Branch (1013:7): [True: 744, False: 4]
  ------------------
 1014|    744|    result scalar_res =
 1015|    744|        scalar::utf32_to_utf16::convert_with_errors<endianness::LITTLE>(
 1016|    744|            buf + ret.first.count, len - ret.first.count, ret.second);
 1017|    744|    if (scalar_res.error) {
  ------------------
  |  Branch (1017:9): [True: 568, False: 176]
  ------------------
 1018|    568|      scalar_res.count += ret.first.count;
 1019|    568|      return scalar_res;
 1020|    568|    } else {
 1021|    176|      ret.second += scalar_res.count;
 1022|    176|    }
 1023|    744|  }
 1024|    180|  ret.first.count =
 1025|    180|      ret.second -
 1026|    180|      utf16_output; // Set count to the number of 8-bit code units written
 1027|    180|  return ret.first;
 1028|    748|}
_ZNK7simdutf7haswell14implementation36convert_utf32_to_utf16be_with_errorsEPKDimPDs:
 1031|    670|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1032|       |  // ret.first.count is always the position in the buffer, not the number of
 1033|       |  // code units written even if finished
 1034|    670|  std::pair<result, char16_t *> ret =
 1035|    670|      haswell::avx2_convert_utf32_to_utf16_with_errors<endianness::BIG>(
 1036|    670|          buf, len, utf16_output);
 1037|    670|  if (ret.first.count != len) {
  ------------------
  |  Branch (1037:7): [True: 666, False: 4]
  ------------------
 1038|    666|    result scalar_res =
 1039|    666|        scalar::utf32_to_utf16::convert_with_errors<endianness::BIG>(
 1040|    666|            buf + ret.first.count, len - ret.first.count, ret.second);
 1041|    666|    if (scalar_res.error) {
  ------------------
  |  Branch (1041:9): [True: 504, False: 162]
  ------------------
 1042|    504|      scalar_res.count += ret.first.count;
 1043|    504|      return scalar_res;
 1044|    504|    } else {
 1045|    162|      ret.second += scalar_res.count;
 1046|    162|    }
 1047|    666|  }
 1048|    166|  ret.first.count =
 1049|    166|      ret.second -
 1050|    166|      utf16_output; // Set count to the number of 8-bit code units written
 1051|    166|  return ret.first;
 1052|    670|}
_ZNK7simdutf7haswell14implementation30convert_valid_utf32_to_utf16leEPKDimPDs:
 1055|    320|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1056|    320|  return convert_utf32_to_utf16le(buf, len, utf16_output);
 1057|    320|}
_ZNK7simdutf7haswell14implementation30convert_valid_utf32_to_utf16beEPKDimPDs:
 1060|    132|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1061|    132|  return convert_utf32_to_utf16be(buf, len, utf16_output);
 1062|    132|}
_ZNK7simdutf7haswell14implementation30convert_valid_utf16le_to_utf32EPKDsmPDi:
 1065|    128|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
 1066|    128|  return convert_utf16le_to_utf32(buf, len, utf32_output);
 1067|    128|}
_ZNK7simdutf7haswell14implementation30convert_valid_utf16be_to_utf32EPKDsmPDi:
 1070|    180|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
 1071|    180|  return convert_utf16be_to_utf32(buf, len, utf32_output);
 1072|    180|}
_ZNK7simdutf7haswell14implementation13count_utf16leEPKDsm:
 1083|  1.43k|    const char16_t *input, size_t length) const noexcept {
 1084|  1.43k|  return utf16::count_code_points<endianness::LITTLE>(input, length);
 1085|  1.43k|}
_ZNK7simdutf7haswell14implementation13count_utf16beEPKDsm:
 1088|  1.32k|    const char16_t *input, size_t length) const noexcept {
 1089|  1.32k|  return utf16::count_code_points<endianness::BIG>(input, length);
 1090|  1.32k|}
_ZNK7simdutf7haswell14implementation10count_utf8EPKcm:
 1095|  4.92k|implementation::count_utf8(const char *in, size_t size) const noexcept {
 1096|  4.92k|  return utf8::count_code_points_bytemask(in, size);
 1097|  4.92k|}
_ZNK7simdutf7haswell14implementation23latin1_length_from_utf8EPKcm:
 1107|    720|    const char *buf, size_t len) const noexcept {
 1108|    720|  return count_utf8(buf, len);
 1109|    720|}
_ZNK7simdutf7haswell14implementation24utf8_length_from_utf16leEPKDsm:
 1114|    793|    const char16_t *input, size_t length) const noexcept {
 1115|    793|  return utf16::utf8_length_from_utf16_bytemask<endianness::LITTLE>(input,
 1116|    793|                                                                    length);
 1117|    793|}
_ZNK7simdutf7haswell14implementation24utf8_length_from_utf16beEPKDsm:
 1120|    723|    const char16_t *input, size_t length) const noexcept {
 1121|    723|  return utf16::utf8_length_from_utf16_bytemask<endianness::BIG>(input, length);
 1122|    723|}
_ZNK7simdutf7haswell14implementation25utf32_length_from_utf16leEPKDsm:
 1127|    442|    const char16_t *input, size_t length) const noexcept {
 1128|    442|  return utf16::utf32_length_from_utf16<endianness::LITTLE>(input, length);
 1129|    442|}
_ZNK7simdutf7haswell14implementation25utf32_length_from_utf16beEPKDsm:
 1132|    426|    const char16_t *input, size_t length) const noexcept {
 1133|    426|  return utf16::utf32_length_from_utf16<endianness::BIG>(input, length);
 1134|    426|}
_ZNK7simdutf7haswell14implementation22utf16_length_from_utf8EPKcm:
 1139|  2.28k|    const char *input, size_t length) const noexcept {
 1140|  2.28k|  return utf8::utf16_length_from_utf8_bytemask(input, length);
 1141|  2.28k|}
_ZNK7simdutf7haswell14implementation23utf8_length_from_latin1EPKcm:
 1180|    280|    const char *input, size_t len) const noexcept {
 1181|    280|  const uint8_t *data = reinterpret_cast<const uint8_t *>(input);
 1182|    280|  size_t answer = len / sizeof(__m256i) * sizeof(__m256i);
 1183|    280|  size_t i = 0;
 1184|    280|  if (answer >= 2048) { // long strings optimization
  ------------------
  |  Branch (1184:7): [True: 133, False: 147]
  ------------------
 1185|    133|    __m256i four_64bits = _mm256_setzero_si256();
 1186|  3.21k|    while (i + sizeof(__m256i) <= len) {
  ------------------
  |  Branch (1186:12): [True: 3.08k, False: 133]
  ------------------
 1187|  3.08k|      __m256i runner = _mm256_setzero_si256();
 1188|       |      // We can do up to 255 loops without overflow.
 1189|  3.08k|      size_t iterations = (len - i) / sizeof(__m256i);
 1190|  3.08k|      if (iterations > 255) {
  ------------------
  |  Branch (1190:11): [True: 2.95k, False: 133]
  ------------------
 1191|  2.95k|        iterations = 255;
 1192|  2.95k|      }
 1193|  3.08k|      size_t max_i = i + iterations * sizeof(__m256i) - sizeof(__m256i);
 1194|   191k|      for (; i + 4 * sizeof(__m256i) <= max_i; i += 4 * sizeof(__m256i)) {
  ------------------
  |  Branch (1194:14): [True: 188k, False: 3.08k]
  ------------------
 1195|   188k|        __m256i input1 = _mm256_loadu_si256((const __m256i *)(data + i));
 1196|   188k|        __m256i input2 =
 1197|   188k|            _mm256_loadu_si256((const __m256i *)(data + i + sizeof(__m256i)));
 1198|   188k|        __m256i input3 = _mm256_loadu_si256(
 1199|   188k|            (const __m256i *)(data + i + 2 * sizeof(__m256i)));
 1200|   188k|        __m256i input4 = _mm256_loadu_si256(
 1201|   188k|            (const __m256i *)(data + i + 3 * sizeof(__m256i)));
 1202|   188k|        __m256i input12 =
 1203|   188k|            _mm256_add_epi8(_mm256_cmpgt_epi8(_mm256_setzero_si256(), input1),
 1204|   188k|                            _mm256_cmpgt_epi8(_mm256_setzero_si256(), input2));
 1205|   188k|        __m256i input23 =
 1206|   188k|            _mm256_add_epi8(_mm256_cmpgt_epi8(_mm256_setzero_si256(), input3),
 1207|   188k|                            _mm256_cmpgt_epi8(_mm256_setzero_si256(), input4));
 1208|   188k|        __m256i input1234 = _mm256_add_epi8(input12, input23);
 1209|   188k|        runner = _mm256_sub_epi8(runner, input1234);
 1210|   188k|      }
 1211|  12.2k|      for (; i <= max_i; i += sizeof(__m256i)) {
  ------------------
  |  Branch (1211:14): [True: 9.20k, False: 3.08k]
  ------------------
 1212|  9.20k|        __m256i input_256_chunk =
 1213|  9.20k|            _mm256_loadu_si256((const __m256i *)(data + i));
 1214|  9.20k|        runner = _mm256_sub_epi8(
 1215|  9.20k|            runner, _mm256_cmpgt_epi8(_mm256_setzero_si256(), input_256_chunk));
 1216|  9.20k|      }
 1217|  3.08k|      four_64bits = _mm256_add_epi64(
 1218|  3.08k|          four_64bits, _mm256_sad_epu8(runner, _mm256_setzero_si256()));
 1219|  3.08k|    }
 1220|    133|    answer += _mm256_extract_epi64(four_64bits, 0) +
 1221|    133|              _mm256_extract_epi64(four_64bits, 1) +
 1222|    133|              _mm256_extract_epi64(four_64bits, 2) +
 1223|    133|              _mm256_extract_epi64(four_64bits, 3);
 1224|    147|  } else if (answer > 0) {
  ------------------
  |  Branch (1224:14): [True: 76, False: 71]
  ------------------
 1225|    998|    for (; i + sizeof(__m256i) <= len; i += sizeof(__m256i)) {
  ------------------
  |  Branch (1225:12): [True: 922, False: 76]
  ------------------
 1226|    922|      __m256i latin = _mm256_loadu_si256((const __m256i *)(data + i));
 1227|    922|      uint32_t non_ascii = _mm256_movemask_epi8(latin);
 1228|    922|      answer += count_ones(non_ascii);
 1229|    922|    }
 1230|     76|  }
 1231|    280|  return answer + scalar::latin1::utf8_length_from_latin1(
 1232|    280|                      reinterpret_cast<const char *>(data + i), len - i);
 1233|    280|}
_ZNK7simdutf7haswell14implementation22utf8_length_from_utf32EPKDim:
 1238|    913|    const char32_t *input, size_t length) const noexcept {
 1239|    913|  return utf32::utf8_length_from_utf32(input, length);
 1240|    913|}
_ZNK7simdutf7haswell14implementation23utf16_length_from_utf32EPKDim:
 1245|  1.43k|    const char32_t *input, size_t length) const noexcept {
 1246|  1.43k|  const __m256i v_00000000 = _mm256_setzero_si256();
 1247|  1.43k|  const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000);
 1248|  1.43k|  size_t pos = 0;
 1249|  1.43k|  size_t count = 0;
 1250|   482k|  for (; pos + 8 <= length; pos += 8) {
  ------------------
  |  Branch (1250:10): [True: 481k, False: 1.43k]
  ------------------
 1251|   481k|    __m256i in = _mm256_loadu_si256((__m256i *)(input + pos));
 1252|   481k|    const __m256i surrogate_bytemask =
 1253|   481k|        _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffff0000), v_00000000);
 1254|   481k|    const uint32_t surrogate_bitmask =
 1255|   481k|        static_cast<uint32_t>(_mm256_movemask_epi8(surrogate_bytemask));
 1256|   481k|    size_t surrogate_count = (32 - count_ones(surrogate_bitmask)) / 4;
 1257|   481k|    count += 8 + surrogate_count;
 1258|   481k|  }
 1259|  1.43k|  return count +
 1260|  1.43k|         scalar::utf32::utf16_length_from_utf32(input + pos, length - pos);
 1261|  1.43k|}
_ZNK7simdutf7haswell14implementation22utf32_length_from_utf8EPKcm:
 1266|  1.20k|    const char *input, size_t length) const noexcept {
 1267|  1.20k|  return utf8::count_code_points(input, length);
 1268|  1.20k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_18is_asciiERKNS1_4simd8simd8x64IhEE:
   13|  5.54M|simdutf_really_inline bool is_ascii(const simd8x64<uint8_t> &input) {
   14|  5.54M|  return input.reduce_or().is_ascii();
   15|  5.54M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_124must_be_2_3_continuationENS1_4simd5simd8IhEES4_:
   22|  3.98M|                         const simd8<uint8_t> prev3) {
   23|  3.98M|  simd8<uint8_t> is_third_byte =
   24|  3.98M|      prev2.saturating_sub(0xe0u - 0x80); // Only 111_____ will be > 0x80
   25|  3.98M|  simd8<uint8_t> is_fourth_byte =
   26|  3.98M|      prev3.saturating_sub(0xf0u - 0x80); // Only 1111____ will be > 0x80
   27|  3.98M|  return simd8<bool>(is_third_byte | is_fourth_byte);
   28|  3.98M|}
_ZN7simdutf7haswell34convert_utf16_to_utf8_with_detailsILNS_10endiannessE0EEENS_11full_resultEPKDsmPc:
  722|    686|    const char16_t *buf, size_t len, char *utf8_output) {
  723|    686|  std::pair<result, char *> ret =
  724|    686|      haswell::avx2_convert_utf16_to_utf8_with_errors<big_endian>(buf, len,
  725|    686|                                                                  utf8_output);
  726|    686|  if (ret.first.error) {
  ------------------
  |  Branch (726:7): [True: 110, False: 576]
  ------------------
  727|    110|    return full_result(ret.first.error, ret.first.count,
  728|    110|                       size_t(ret.second - utf8_output));
  729|    110|  }
  730|    576|  if (ret.first.count != len) {
  ------------------
  |  Branch (730:7): [True: 572, False: 4]
  ------------------
  731|    572|    full_result sres =
  732|    572|        scalar::utf16_to_utf8::convert_with_errors<big_endian, false>(
  733|    572|            buf + ret.first.count, len - ret.first.count, ret.second, 0);
  734|    572|    return full_result(sres.error, ret.first.count + sres.input_count,
  735|    572|                       size_t(ret.second - utf8_output) + sres.output_count);
  736|    572|  }
  737|      4|  return full_result(error_code::SUCCESS, len,
  738|      4|                     size_t(ret.second - utf8_output));
  739|    576|}
_ZN7simdutf7haswell34convert_utf16_to_utf8_with_detailsILNS_10endiannessE1EEENS_11full_resultEPKDsmPc:
  722|    556|    const char16_t *buf, size_t len, char *utf8_output) {
  723|    556|  std::pair<result, char *> ret =
  724|    556|      haswell::avx2_convert_utf16_to_utf8_with_errors<big_endian>(buf, len,
  725|    556|                                                                  utf8_output);
  726|    556|  if (ret.first.error) {
  ------------------
  |  Branch (726:7): [True: 146, False: 410]
  ------------------
  727|    146|    return full_result(ret.first.error, ret.first.count,
  728|    146|                       size_t(ret.second - utf8_output));
  729|    146|  }
  730|    410|  if (ret.first.count != len) {
  ------------------
  |  Branch (730:7): [True: 406, False: 4]
  ------------------
  731|    406|    full_result sres =
  732|    406|        scalar::utf16_to_utf8::convert_with_errors<big_endian, false>(
  733|    406|            buf + ret.first.count, len - ret.first.count, ret.second, 0);
  734|    406|    return full_result(sres.error, ret.first.count + sres.input_count,
  735|    406|                       size_t(ret.second - utf8_output) + sres.output_count);
  736|    406|  }
  737|      4|  return full_result(error_code::SUCCESS, len,
  738|      4|                     size_t(ret.second - utf8_output));
  739|    410|}

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

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

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

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

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEC2Ev:
  191|  25.2k|  simdutf_really_inline simd8() : base8_numeric<uint8_t>() {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhEC2Ev:
  124|  25.2k|  simdutf_really_inline base8_numeric() : base8<T>() {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEEC2Ev:
   71|  25.2k|  simdutf_really_inline base8() : base<simd8<T>>() {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEC2Ev:
   14|  25.2k|  simdutf_really_inline base() : value{__m256i()} {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IhE9reduce_orEv:
  294|  5.55M|  simdutf_really_inline simd8<T> reduce_or() const {
  295|  5.55M|    return this->chunks[0] | this->chunks[1];
  296|  5.55M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEorES5_:
   50|  18.1M|  simdutf_really_inline Child operator|(const Child other) const {
   51|  18.1M|    return _mm256_or_si256(*this, other);
   52|  18.1M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEC2EDv4_x:
  193|   195M|      : base8_numeric<uint8_t>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhEC2EDv4_x:
  126|   195M|      : base8<T>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEEC2EDv4_x:
   73|   195M|  simdutf_really_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEC2EDv4_x:
   17|   195M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE8is_asciiEv:
  234|  5.55M|  simdutf_really_inline bool is_ascii() const {
  235|  5.55M|    return _mm256_movemask_epi8(*this) == 0;
  236|  5.55M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEoRES5_:
   59|  8.58M|  simdutf_really_inline Child &operator|=(const Child other) {
   60|  8.58M|    auto this_cast = static_cast<Child *>(this);
   61|  8.58M|    *this_cast = *this_cast | other;
   62|  8.58M|    return *this_cast;
   63|  8.58M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi1EEENS4_IhEES8_:
   83|  4.05M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   84|       |    return _mm256_alignr_epi8(
   85|  4.05M|        *this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N);
   86|  4.05M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE3shrILi4EEES4_v:
  245|  8.11M|  template <int N> simdutf_really_inline simd8<uint8_t> shr() const {
  246|  8.11M|    return simd8<uint8_t>(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N);
  247|  8.11M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE9lookup_16IhEENS2_5simd8IT_EES7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_:
  157|  12.1M|            L replace15) const {
  158|  12.1M|    return lookup_16(simd8<L>::repeat_16(
  159|  12.1M|        replace0, replace1, replace2, replace3, replace4, replace5, replace6,
  160|  12.1M|        replace7, replace8, replace9, replace10, replace11, replace12,
  161|  12.1M|        replace13, replace14, replace15));
  162|  12.1M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE9lookup_16IhEENS2_5simd8IT_EES8_:
  148|  12.1M|  simdutf_really_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
  149|  12.1M|    return _mm256_shuffle_epi8(lookup_table, *this);
  150|  12.1M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE9repeat_16Ehhhhhhhhhhhhhhhh:
  118|  12.1M|                                                  T v14, T v15) {
  119|  12.1M|    return simd8<T>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
  120|  12.1M|                    v14, v15, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
  121|  12.1M|                    v12, v13, v14, v15);
  122|  12.1M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEC2Ehhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh:
  207|  12.1M|      : simd8(_mm256_setr_epi8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
  208|  12.1M|                               v12, v13, v14, v15, v16, v17, v18, v19, v20, v21,
  209|  12.1M|                               v22, v23, v24, v25, v26, v27, v28, v29, v30,
  210|  12.1M|                               v31)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEC2Eh:
  195|  28.7M|  simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi2EEENS4_IhEES8_:
   83|  3.98M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   84|       |    return _mm256_alignr_epi8(
   85|  3.98M|        *this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N);
   86|  3.98M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi3EEENS4_IhEES8_:
   83|  3.98M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   84|       |    return _mm256_alignr_epi8(
   85|  3.98M|        *this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N);
   86|  3.98M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE14saturating_subES4_:
  214|  9.00M|  saturating_sub(const simd8<uint8_t> other) const {
  215|  9.00M|    return _mm256_subs_epu8(*this, other);
  216|  9.00M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IbEC2EDv4_x:
   95|  28.7M|  simdutf_really_inline simd8(const __m256i _value) : base8<bool>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5base8IbNS2_5simd8IbEEEC2EDv4_x:
   73|  28.7M|  simdutf_really_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IbEEEC2EDv4_x:
   17|  28.7M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IbEEEcvRKDv4_xEv:
   19|  18.8M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEeoES5_:
   56|  3.98M|  simdutf_really_inline Child operator^(const Child other) const {
   57|  3.98M|    return _mm256_xor_si256(*this, other);
   58|  3.98M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEC2EPKh:
  197|  1.02M|  simdutf_really_inline simd8(const uint8_t values[32]) : simd8(load(values)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE4loadEPKh:
  111|  12.1M|  static simdutf_really_inline simd8<T> load(const T values[32]) {
  112|  12.1M|    return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(values));
  113|  12.1M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE7gt_bitsES4_:
  225|  1.02M|  gt_bits(const simd8<uint8_t> other) const {
  226|  1.02M|    return this->saturating_sub(other);
  227|  1.02M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEC2EDv4_x:
   17|  72.5M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEcvRKDv4_xEv:
   19|   102M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IbE10to_bitmaskEv:
   99|  9.82M|  simdutf_really_inline uint32_t to_bitmask() const {
  100|  9.82M|    return uint32_t(_mm256_movemask_epi8(value));
  101|  9.82M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE21any_bits_set_anywhereEv:
  241|  2.53M|  simdutf_really_inline bool any_bits_set_anywhere() const {
  242|  2.53M|    return !bits_not_set_anywhere();
  243|  2.53M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE21bits_not_set_anywhereEv:
  237|  2.53M|  simdutf_really_inline bool bits_not_set_anywhere() const {
  238|  2.53M|    return _mm256_testz_si256(*this, *this);
  239|  2.53M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IhE8is_asciiEv:
  298|  2.63k|  simdutf_really_inline bool is_ascii() const {
  299|  2.63k|    return this->reduce_or().is_ascii();
  300|  2.63k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IhE13gteq_unsignedEh:
  341|  1.75k|  simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const {
  342|  1.75k|    const simd8<uint8_t> mask = simd8<uint8_t>::splat(m);
  343|  1.75k|    return simd8x64<bool>((simd8<uint8_t>(__m256i(this->chunks[0])) >= mask),
  344|  1.75k|                          (simd8<uint8_t>(__m256i(this->chunks[1])) >= mask))
  345|  1.75k|        .to_bitmask();
  346|  1.75k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEgeES4_:
  229|  3.61M|  operator>=(const simd8<uint8_t> other) const {
  230|  3.61M|    return other.min_val(*this) == other;
  231|  3.61M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE7min_valES4_:
  220|  3.61M|  min_val(const simd8<uint8_t> other) const {
  221|  3.61M|    return _mm256_min_epu8(other, *this);
  222|  3.61M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IbEC2ENS2_5simd8IbEES6_:
  272|  2.20M|      : chunks{chunk0, chunk1} {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IbE10to_bitmaskEv:
  282|  2.20M|  simdutf_really_inline uint64_t to_bitmask() const {
  283|  2.20M|    uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask());
  284|  2.20M|    uint64_t r_hi = this->chunks[1].to_bitmask();
  285|  2.20M|    return r_lo | (r_hi << 32);
  286|  2.20M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaEC2EPKa:
  274|  6.89M|      : chunks{simd8<T>::load(ptr),
  275|  6.89M|               simd8<T>::load(ptr + sizeof(simd8<T>) / sizeof(T))} {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIaE4loadEPKa:
  111|  25.0M|  static simdutf_really_inline simd8<T> load(const T values[32]) {
  112|  25.0M|    return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(values));
  113|  25.0M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IaEC2EDv4_x:
  169|  44.5M|      : base8_numeric<int8_t>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIaEC2EDv4_x:
  126|  44.5M|      : base8<T>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5base8IaNS2_5simd8IbEEEC2EDv4_x:
   73|  44.5M|  simdutf_really_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IaEEEC2EDv4_x:
   17|  44.5M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE8is_asciiEv:
  298|  5.94M|  simdutf_really_inline bool is_ascii() const {
  299|  5.94M|    return this->reduce_or().is_ascii();
  300|  5.94M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE9reduce_orEv:
  294|  5.94M|  simdutf_really_inline simd8<T> reduce_or() const {
  295|  5.94M|    return this->chunks[0] | this->chunks[1];
  296|  5.94M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IaEEEorES5_:
   50|  5.94M|  simdutf_really_inline Child operator|(const Child other) const {
   51|  5.94M|    return _mm256_or_si256(*this, other);
   52|  5.94M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IaEEEcvRKDv4_xEv:
   19|  71.8M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IaE8is_asciiEv:
  177|  5.94M|  simdutf_really_inline bool is_ascii() const {
  178|  5.94M|    return _mm256_movemask_epi8(*this) == 0;
  179|  5.94M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE5storeEPa:
  277|   847k|  simdutf_really_inline void store(T *ptr) const {
  278|   847k|    this->chunks[0].store(ptr + sizeof(simd8<T>) * 0 / sizeof(T));
  279|   847k|    this->chunks[1].store(ptr + sizeof(simd8<T>) * 1 / sizeof(T));
  280|   847k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIaE5storeEPa:
  129|  1.69M|  simdutf_really_inline void store(T dst[32]) const {
  130|  1.69M|    return _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), *this);
  131|  1.69M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IaEcvNS3_IhEEEv:
  256|  3.01M|simdutf_really_inline simd8<int8_t>::operator simd8<uint8_t>() const {
  257|  3.01M|  return this->value;
  258|  3.01M|}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE2ltEa:
  325|  1.25M|  simdutf_really_inline uint64_t lt(const T m) const {
  326|  1.25M|    const simd8<T> mask = simd8<T>::splat(m);
  327|  1.25M|    return simd8x64<bool>(this->chunks[0] < mask, this->chunks[1] < mask)
  328|  1.25M|        .to_bitmask();
  329|  1.25M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIaE5splatEa:
  105|  13.5M|  static simdutf_really_inline simd8<T> splat(T _value) {
  106|  13.5M|    return _mm256_set1_epi8(_value);
  107|  13.5M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IaEltES4_:
  184|  2.51M|  simdutf_really_inline simd8<bool> operator<(const simd8<int8_t> other) const {
  185|  2.51M|    return _mm256_cmpgt_epi8(other, *this);
  186|  2.51M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE20store_ascii_as_utf32EPDi:
  310|  1.38M|  simdutf_really_inline void store_ascii_as_utf32(char32_t *ptr) const {
  311|  1.38M|    this->chunks[0].store_ascii_as_utf32(ptr + sizeof(simd8<T>) * 0);
  312|  1.38M|    this->chunks[1].store_ascii_as_utf32(ptr + sizeof(simd8<T>) * 1);
  313|  1.38M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IaEEE20store_ascii_as_utf32EPDi:
   36|  2.76M|  simdutf_really_inline void store_ascii_as_utf32(char32_t *ptr) const {
   37|  2.76M|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr),
   38|  2.76M|                        _mm256_cvtepu8_epi32(_mm256_castsi256_si128(*this)));
   39|  2.76M|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr + 8),
   40|  2.76M|                        _mm256_cvtepu8_epi32(_mm256_castsi256_si128(
   41|  2.76M|                            _mm256_srli_si256(*this, 8))));
   42|  2.76M|    _mm256_storeu_si256(
   43|  2.76M|        reinterpret_cast<__m256i *>(ptr + 16),
   44|  2.76M|        _mm256_cvtepu8_epi32(_mm256_extractf128_si256(*this, 1)));
   45|  2.76M|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr + 24),
   46|       |                        _mm256_cvtepu8_epi32(_mm_srli_si128(
   47|  2.76M|                            _mm256_extractf128_si256(*this, 1), 8)));
   48|  2.76M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE4zeroEv:
  108|   124k|  static simdutf_really_inline simd8<T> zero() {
  109|   124k|    return _mm256_setzero_si256();
  110|   124k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IaEgtES4_:
  181|  13.1M|  simdutf_really_inline simd8<bool> operator>(const simd8<int8_t> other) const {
  182|  13.1M|    return _mm256_cmpgt_epi8(*this, other);
  183|  13.1M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IaEC2Ea:
  172|  11.2M|  simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhEmIENS2_5simd8IhEE:
  137|  14.9M|  simdutf_really_inline simd8<T> &operator-=(const simd8<T> other) {
  138|  14.9M|    *this = *this - other;
  139|  14.9M|    return *static_cast<simd8<T> *>(this);
  140|  14.9M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhEmiENS2_5simd8IhEE:
  134|  14.9M|  simdutf_really_inline simd8<T> operator-(const simd8<T> other) const {
  135|  14.9M|    return _mm256_sub_epi8(*this, other);
  136|  14.9M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd10sum_8bytesENS2_5simd8IhEE:
  353|  58.4k|simdutf_really_inline simd64<uint64_t> sum_8bytes(const simd8<uint8_t> v) {
  354|  58.4k|  return _mm256_sad_epu8(v.value, simd8<uint8_t>::zero());
  355|  58.4k|}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE9sum_bytesEv:
  249|  3.33k|  simdutf_really_inline uint64_t sum_bytes() const {
  250|  3.33k|    const auto tmp = _mm256_sad_epu8(value, _mm256_setzero_si256());
  251|       |
  252|  3.33k|    return _mm256_extract_epi64(tmp, 0) + _mm256_extract_epi64(tmp, 1) +
  253|  3.33k|           _mm256_extract_epi64(tmp, 2) + _mm256_extract_epi64(tmp, 3);
  254|  3.33k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE2gtEa:
  331|   944k|  simdutf_really_inline uint64_t gt(const T m) const {
  332|   944k|    const simd8<T> mask = simd8<T>::splat(m);
  333|   944k|    return simd8x64<bool>(this->chunks[0] > mask, this->chunks[1] > mask)
  334|   944k|        .to_bitmask();
  335|   944k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEC2EDv4_x:
   17|  14.6M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEcvRKDv4_xEv:
   19|  14.6M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE5splatEh:
  105|  28.7M|  static simdutf_really_inline simd8<T> splat(T _value) {
  106|  28.7M|    return _mm256_set1_epi8(_value);
  107|  28.7M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEcvRKDv4_xEv:
   19|   259M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simdeqENS2_5simd8IhEES4_:
   76|  9.03M|                                               const simd8<T> rhs) {
   77|  9.03M|    return _mm256_cmpeq_epi8(lhs, rhs);
   78|  9.03M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IhEC2EPKh:
  274|  5.54M|      : chunks{simd8<T>::load(ptr),
  275|  5.54M|               simd8<T>::load(ptr + sizeof(simd8<T>) / sizeof(T))} {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEanES5_:
   53|  29.6M|  simdutf_really_inline Child operator&(const Child other) const {
   54|  29.6M|    return _mm256_and_si256(*this, other);
   55|  29.6M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEanES5_:
   53|  11.3M|  simdutf_really_inline Child operator&(const Child other) const {
   54|  11.3M|    return _mm256_and_si256(*this, other);
   55|  11.3M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE20store_ascii_as_utf16ILNS_10endiannessE0EEEvPDs:
  303|  1.14M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const {
  304|  1.14M|    this->chunks[0].template store_ascii_as_utf16<endian>(ptr +
  305|  1.14M|                                                          sizeof(simd8<T>) * 0);
  306|  1.14M|    this->chunks[1].template store_ascii_as_utf16<endian>(ptr +
  307|  1.14M|                                                          sizeof(simd8<T>) * 1);
  308|  1.14M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IaEEE20store_ascii_as_utf16ILNS_10endiannessE0EEEvPDs:
   22|  2.28M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const {
   23|  2.28M|    __m256i first = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(*this));
   24|  2.28M|    __m256i second = _mm256_cvtepu8_epi16(_mm256_extractf128_si256(*this, 1));
   25|  2.28M|    if (big_endian) {
  ------------------
  |  Branch (25:9): [Folded, False: 2.28M]
  ------------------
   26|      0|      const __m256i swap = _mm256_setr_epi8(
   27|      0|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   28|      0|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   29|      0|      first = _mm256_shuffle_epi8(first, swap);
   30|      0|      second = _mm256_shuffle_epi8(second, swap);
   31|      0|    }
   32|  2.28M|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr), first);
   33|  2.28M|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr + 16), second);
   34|  2.28M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE20store_ascii_as_utf16ILNS_10endiannessE1EEEvPDs:
  303|  1.31M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const {
  304|  1.31M|    this->chunks[0].template store_ascii_as_utf16<endian>(ptr +
  305|  1.31M|                                                          sizeof(simd8<T>) * 0);
  306|  1.31M|    this->chunks[1].template store_ascii_as_utf16<endian>(ptr +
  307|  1.31M|                                                          sizeof(simd8<T>) * 1);
  308|  1.31M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IaEEE20store_ascii_as_utf16ILNS_10endiannessE1EEEvPDs:
   22|  2.63M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const {
   23|  2.63M|    __m256i first = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(*this));
   24|  2.63M|    __m256i second = _mm256_cvtepu8_epi16(_mm256_extractf128_si256(*this, 1));
   25|  2.63M|    if (big_endian) {
  ------------------
  |  Branch (25:9): [True: 2.63M, Folded]
  ------------------
   26|  2.63M|      const __m256i swap = _mm256_setr_epi8(
   27|  2.63M|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   28|  2.63M|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   29|  2.63M|      first = _mm256_shuffle_epi8(first, swap);
   30|  2.63M|      second = _mm256_shuffle_epi8(second, swap);
   31|  2.63M|    }
   32|  2.63M|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr), first);
   33|  2.63M|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr + 16), second);
   34|  2.63M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEorES5_:
   50|  4.18M|  simdutf_really_inline Child operator|(const Child other) const {
   51|  4.18M|    return _mm256_or_si256(*this, other);
   52|  4.18M|  }

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd16ItEC2EDv4_x:
   97|  72.5M|      : base16_numeric<uint16_t>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItEC2EDv4_x:
   73|  72.5M|      : base16<T>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6base16ItNS2_6simd16IbEEEC2EDv4_x:
   20|  72.5M|      : base<simd16<T>>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd16ItE4packERKS4_S6_:
  149|  5.35M|                                                   const simd16<uint16_t> &v1) {
  150|       |    // Note: the AVX2 variant of pack operates on 128-bit lanes, thus
  151|       |    //       we have to shuffle lanes in order to produce bytes in the
  152|       |    //       correct order.
  153|       |
  154|       |    // get the 0th lanes
  155|  5.35M|    const __m128i lo_0 = _mm256_extracti128_si256(v0, 0);
  156|  5.35M|    const __m128i lo_1 = _mm256_extracti128_si256(v1, 0);
  157|       |
  158|       |    // get the 1st lanes
  159|  5.35M|    const __m128i hi_0 = _mm256_extracti128_si256(v0, 1);
  160|  5.35M|    const __m128i hi_1 = _mm256_extracti128_si256(v1, 1);
  161|       |
  162|       |    // build new vectors (shuffle lanes)
  163|  5.35M|    const __m256i t0 = _mm256_set_m128i(lo_1, lo_0);
  ------------------
  |  |    4|  5.35M|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    5|  5.35M|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  164|  5.35M|    const __m256i t1 = _mm256_set_m128i(hi_1, hi_0);
  ------------------
  |  |    4|  5.35M|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    5|  5.35M|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  165|       |
  166|       |    // pack code units in linear order from v0 and v1
  167|  5.35M|    return _mm256_packus_epi16(t0, t1);
  168|  5.35M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd9simd16x32ItEC2EPKt:
  202|  2.09M|      : chunks{simd16<T>::load(ptr),
  203|  2.09M|               simd16<T>::load(ptr + sizeof(simd16<T>) / sizeof(T))} {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItE4loadEPKt:
   66|  16.9M|  static simdutf_really_inline simd16<T> load(const T values[8]) {
   67|  16.9M|    return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(values));
   68|  16.9M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd9simd16x32ItE10swap_bytesEv:
  229|  1.13M|  simdutf_really_inline void swap_bytes() {
  230|  1.13M|    this->chunks[0] = this->chunks[0].swap_bytes();
  231|  1.13M|    this->chunks[1] = this->chunks[1].swap_bytes();
  232|  1.13M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd16ItE10swap_bytesEv:
  139|  3.21M|  simdutf_really_inline simd16<uint16_t> swap_bytes() const {
  140|  3.21M|    const __m256i swap = _mm256_setr_epi8(
  141|  3.21M|        1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
  142|  3.21M|        21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
  143|  3.21M|    return _mm256_shuffle_epi8(*this, swap);
  144|  3.21M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItE5splatEt:
   58|  14.9M|  static simdutf_really_inline simd16<T> splat(T _value) {
   59|  14.9M|    return _mm256_set1_epi16(_value);
   60|  14.9M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simdeqENS2_6simd16ItEES4_:
   26|  10.4M|                                               const simd16<T> rhs) {
   27|  10.4M|    return _mm256_cmpeq_epi16(lhs, rhs);
   28|  10.4M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd16IbEC2EDv4_x:
   45|  14.6M|  simdutf_really_inline simd16(const __m256i _value) : base16<bool>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6base16IbNS2_6simd16IbEEEC2EDv4_x:
   20|  14.6M|      : base<simd16<T>>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd9simd16x32IbEC2ENS2_6simd16IbEES6_:
  200|  2.09M|      : chunks{chunk0, chunk1} {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd9simd16x32IbE10to_bitmaskEv:
  210|  2.09M|  simdutf_really_inline uint64_t to_bitmask() const {
  211|  2.09M|    uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask());
  212|  2.09M|    uint64_t r_hi = this->chunks[1].to_bitmask();
  213|  2.09M|    return r_lo | (r_hi << 32);
  214|  2.09M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd16IbE10to_bitmaskEv:
   50|  4.18M|  simdutf_really_inline bitmask_type to_bitmask() const {
   51|  4.18M|    return _mm256_movemask_epi8(*this);
   52|  4.18M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd16ItE3shrILi8EEES4_v:
  134|  5.43M|  template <int N> simdutf_really_inline simd16<uint16_t> shr() const {
  135|  5.43M|    return simd16<uint16_t>(_mm256_srli_epi16(*this, N));
  136|  5.43M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd16ItEleES4_:
  117|  4.18M|  operator<=(const simd16<uint16_t> other) const {
  118|  4.18M|    return other.max_val(*this) == other;
  119|  4.18M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd16ItE7max_valES4_:
  108|  4.18M|  max_val(const simd16<uint16_t> other) const {
  109|  4.18M|    return _mm256_max_epu16(*this, other);
  110|  4.18M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd16ItEC2EPKDs:
  104|  10.7M|      : simd16(load(reinterpret_cast<const uint16_t *>(values))) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd16ItEC2Et:
  100|  10.7M|  simdutf_really_inline simd16(uint16_t _value) : simd16(splat(_value)) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd9simd16x32ItE12not_in_rangeEtt:
  249|  2.09M|  simdutf_really_inline uint64_t not_in_range(const T low, const T high) const {
  250|  2.09M|    const simd16<T> mask_low = simd16<T>::splat(static_cast<T>(low - 1));
  251|  2.09M|    const simd16<T> mask_high = simd16<T>::splat(static_cast<T>(high + 1));
  252|  2.09M|    return simd16x32<bool>(
  253|  2.09M|               (this->chunks[0] >= mask_high) | (this->chunks[0] <= mask_low),
  254|  2.09M|               (this->chunks[1] >= mask_high) | (this->chunks[1] <= mask_low))
  255|  2.09M|        .to_bitmask();
  256|  2.09M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd16ItEgeES4_:
  121|  4.18M|  operator>=(const simd16<uint16_t> other) const {
  122|  4.18M|    return other.min_val(*this) == other;
  123|  4.18M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd16ItE7min_valES4_:
  112|  4.18M|  min_val(const simd16<uint16_t> other) const {
  113|  4.18M|    return _mm256_min_epu16(*this, other);
  114|  4.18M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItE4zeroEv:
   62|  1.55k|  static simdutf_really_inline simd16<T> zero() {
   63|  1.55k|    return _mm256_setzero_si256();
   64|  1.55k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd3minENS2_6simd16ItEES4_:
  259|  4.06M|simd16<uint16_t> min(const simd16<uint16_t> a, simd16<uint16_t> b) {
  260|  4.06M|  return _mm256_min_epu16(a.value, b.value);
  261|  4.06M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItEpLENS2_6simd16ItEE:
   87|  6.10M|  simdutf_really_inline simd16<T> &operator+=(const simd16<T> other) {
   88|  6.10M|    *this = *this + other;
   89|  6.10M|    return *static_cast<simd16<T> *>(this);
   90|  6.10M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd14base16_numericItEplENS2_6simd16ItEE:
   84|  6.10M|  simdutf_really_inline simd16<T> operator+(const simd16<T> other) const {
   85|  6.10M|    return _mm256_add_epi16(*this, other);
   86|  6.10M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd16ItE3sumEv:
  170|  1.55k|  simdutf_really_inline uint64_t sum() const {
  171|  1.55k|    const auto lo_u16 = _mm256_and_si256(value, _mm256_set1_epi32(0x0000ffff));
  172|  1.55k|    const auto hi_u16 = _mm256_srli_epi32(value, 16);
  173|  1.55k|    const auto sum_u32 = _mm256_add_epi32(lo_u16, hi_u16);
  174|       |
  175|  1.55k|    const auto lo_u32 =
  176|  1.55k|        _mm256_and_si256(sum_u32, _mm256_set1_epi64x(0xffffffff));
  177|  1.55k|    const auto hi_u32 = _mm256_srli_epi64(sum_u32, 32);
  178|  1.55k|    const auto sum_u64 = _mm256_add_epi64(lo_u32, hi_u32);
  179|       |
  180|  1.55k|    return uint64_t(_mm256_extract_epi64(sum_u64, 0)) +
  181|  1.55k|           uint64_t(_mm256_extract_epi64(sum_u64, 1)) +
  182|  1.55k|           uint64_t(_mm256_extract_epi64(sum_u64, 2)) +
  183|       |           uint64_t(_mm256_extract_epi64(sum_u64, 3));
  184|  1.55k|  }

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd32IjE5splatEj:
   46|  19.4k|  simdutf_really_inline static simd32<uint32_t> splat(uint32_t v) {
   47|  19.4k|    return _mm256_set1_epi32(v);
   48|  19.4k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd32IjEC2EDv4_x:
    9|  7.37M|  simdutf_really_inline simd32(const __m256i v) : value(v) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd32IjE4zeroEv:
   42|  5.91k|  simdutf_really_inline static simd32<uint32_t> zero() {
   43|  5.91k|    return _mm256_setzero_si256();
   44|  5.91k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd32IjEC2IDiEEPKT_:
   13|  1.72M|      : value(_mm256_loadu_si256(reinterpret_cast<const __m256i *>(ptr))) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd32IjE10swap_bytesEv:
   27|  1.35M|  simdutf_really_inline simd32<uint32_t> swap_bytes() const {
   28|  1.35M|    const __m256i shuffle =
   29|  1.35M|        _mm256_setr_epi8(3, 2, 1, 0, 7, 6, 5, 4, 8, 9, 10, 11, 15, 14, 13, 12,
   30|  1.35M|                         3, 2, 1, 0, 7, 6, 5, 4, 8, 9, 10, 11, 15, 14, 13, 12);
   31|       |
   32|  1.35M|    return _mm256_shuffle_epi8(value, shuffle);
   33|  1.35M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd3maxENS2_6simd32IjEES4_:
   80|  2.42M|                                           const simd32<uint32_t> b) {
   81|  2.42M|  return _mm256_max_epu32(a.value, b.value);
   82|  2.42M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simdplENS2_6simd32IjEES4_:
   90|  1.21M|                                                 const simd32<uint32_t> b) {
   91|  1.21M|  return _mm256_add_epi32(a.value, b.value);
   92|  1.21M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simdgtENS2_6simd32IjEES4_:
  109|  4.29k|                                             const simd32<uint32_t> b) {
  110|  4.29k|  return !(b >= a);
  111|  4.29k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simdntENS2_6simd32IbEE:
  104|  4.29k|simdutf_really_inline simd32<bool> operator!(const simd32<bool> v) {
  105|  4.29k|  return _mm256_xor_si256(v.value, _mm256_set1_epi8(-1));
  106|  4.29k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd32IbEC2EDv4_x:
   59|   444k|  simdutf_really_inline simd32(const __m256i v) : value(v) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simdgeENS2_6simd32IjEES4_:
  100|   149k|                                              const simd32<uint32_t> b) {
  101|   149k|  return _mm256_cmpeq_epi32(_mm256_max_epu32(a.value, b.value), a.value);
  102|   149k|}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd32IbE3anyEv:
   61|   149k|  simdutf_really_inline bool any() const {
   62|   149k|    return _mm256_movemask_epi8(value) != 0;
   63|   149k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simdeqENS2_6simd32IjEES4_:
   95|   145k|                                              const simd32<uint32_t> b) {
   96|   145k|  return _mm256_cmpeq_epi32(a.value, b.value);
   97|   145k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simdanENS2_6simd32IjEES4_:
   85|  1.24M|                                                 const simd32<uint32_t> a) {
   86|  1.24M|  return _mm256_and_si256(a.value, b.value);
   87|  1.24M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simdorIbEENS2_6simd32IT_EES6_S6_:
   70|   145k|                                          const simd32<T> b) {
   71|   145k|  return _mm256_or_si256(a.value, b.value);
   72|   145k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd3minENS2_6simd32IjEES4_:
   75|  1.10M|                                           const simd32<uint32_t> a) {
   76|  1.10M|  return _mm256_min_epu32(a.value, b.value);
   77|  1.10M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd32IjEpLES4_:
   36|  1.10M|  simdutf_really_inline simd32 &operator+=(const simd32 other) {
   37|  1.10M|    value = _mm256_add_epi32(value, other.value);
   38|  1.10M|    return *this;
   39|  1.10M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd32IjE3sumEv:
   15|    661|  simdutf_really_inline uint64_t sum() const {
   16|    661|    const __m256i mask = _mm256_set1_epi64x(0xffffffff);
   17|    661|    const __m256i t0 = _mm256_and_si256(value, mask);
   18|    661|    const __m256i t1 = _mm256_srli_epi64(value, 32);
   19|    661|    const __m256i t2 = _mm256_add_epi64(t0, t1);
   20|       |
   21|    661|    return uint64_t(_mm256_extract_epi64(t2, 0)) +
   22|    661|           uint64_t(_mm256_extract_epi64(t2, 1)) +
   23|    661|           uint64_t(_mm256_extract_epi64(t2, 2)) +
   24|       |           uint64_t(_mm256_extract_epi64(t2, 3));
   25|    661|  }

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd64ImE4zeroEv:
   27|  7.21k|  simdutf_really_inline static simd64<uint64_t> zero() {
   28|  7.21k|    return _mm256_setzero_si256();
   29|  7.21k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd64ImEC2EDv4_x:
    9|  65.6k|  simdutf_really_inline simd64(const __m256i v) : value(v) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd6simd64ImEpLES4_:
   21|  58.4k|  simdutf_really_inline simd64 &operator+=(const simd64 other) {
   22|  58.4k|    value = _mm256_add_epi64(value, other.value);
   23|  58.4k|    return *this;
   24|  58.4k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd6simd64ImE3sumEv:
   15|  7.21k|  simdutf_really_inline uint64_t sum() const {
   16|  7.21k|    return _mm256_extract_epi64(value, 0) + _mm256_extract_epi64(value, 1) +
   17|  7.21k|           _mm256_extract_epi64(value, 2) + _mm256_extract_epi64(value, 3);
   18|  7.21k|  }

_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|  4.01M|simdutf_really_inline long long int count_ones(uint64_t input_num) {
   15|       |  return _popcnt64(input_num);
   16|  4.01M|}

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

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2Ev:
  183|  25.2k|  simdutf_really_inline simd8() : base8_numeric<uint8_t>() {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhEC2Ev:
  118|  25.2k|  simdutf_really_inline base8_numeric() : base8<T>() {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEEC2Ev:
   68|  25.2k|  simdutf_really_inline base8() : base<simd8<T>>() {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEC2Ev:
   13|  25.2k|  simdutf_really_inline base() : value{__m128i()} {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IhE9reduce_orEv:
  290|  5.55M|  simdutf_really_inline simd8<T> reduce_or() const {
  291|  5.55M|    return (this->chunks[0] | this->chunks[1]) |
  292|  5.55M|           (this->chunks[2] | this->chunks[3]);
  293|  5.55M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEorES5_:
   42|  37.2M|  simdutf_really_inline Child operator|(const Child other) const {
   43|  37.2M|    return _mm_or_si128(*this, other);
   44|  37.2M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2EDv2_x:
  185|   379M|      : base8_numeric<uint8_t>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhEC2EDv2_x:
  120|   379M|      : base8<T>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEEC2EDv2_x:
   69|   379M|  simdutf_really_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEC2EDv2_x:
   16|   379M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE8is_asciiEv:
  228|  5.55M|  simdutf_really_inline bool is_ascii() const {
  229|  5.55M|    return _mm_movemask_epi8(*this) == 0;
  230|  5.55M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEoRES5_:
   51|  12.6M|  simdutf_really_inline Child &operator|=(const Child other) {
   52|  12.6M|    auto this_cast = static_cast<Child *>(this);
   53|  12.6M|    *this_cast = *this_cast | other;
   54|  12.6M|    return *this_cast;
   55|  12.6M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi1EEENS4_IhEES8_:
   79|  8.11M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   80|       |    return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
   81|  8.11M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE3shrILi4EEES4_v:
  238|  16.2M|  template <int N> simdutf_really_inline simd8<uint8_t> shr() const {
  239|  16.2M|    return simd8<uint8_t>(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N);
  240|  16.2M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE9lookup_16IhEENS2_5simd8IT_EES7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_S7_:
  151|  24.3M|            L replace15) const {
  152|  24.3M|    return lookup_16(simd8<L>::repeat_16(
  153|  24.3M|        replace0, replace1, replace2, replace3, replace4, replace5, replace6,
  154|  24.3M|        replace7, replace8, replace9, replace10, replace11, replace12,
  155|  24.3M|        replace13, replace14, replace15));
  156|  24.3M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE9lookup_16IhEENS2_5simd8IT_EES8_:
  142|  24.3M|  simdutf_really_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
  143|  24.3M|    return _mm_shuffle_epi8(lookup_table, *this);
  144|  24.3M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE9repeat_16Ehhhhhhhhhhhhhhhh:
  113|  24.3M|                                                  T v14, T v15) {
  114|  24.3M|    return simd8<T>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
  115|  24.3M|                    v14, v15);
  116|  24.3M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2Ehhhhhhhhhhhhhhhh:
  196|  24.3M|      : simd8(_mm_setr_epi8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
  197|  24.3M|                            v12, v13, v14, v15)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2Eh:
  188|  56.5M|  simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi2EEENS4_IhEES8_:
   79|  7.97M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   80|       |    return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
   81|  7.97M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi3EEENS4_IhEES8_:
   79|  7.97M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   80|       |    return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
   81|  7.97M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE14saturating_subES4_:
  201|  16.9M|  saturating_sub(const simd8<uint8_t> other) const {
  202|  16.9M|    return _mm_subs_epu8(*this, other);
  203|  16.9M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IbEC2EDv2_x:
   91|  52.0M|  simdutf_really_inline simd8(const __m128i _value) : base8<bool>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5base8IbNS2_5simd8IbEEEC2EDv2_x:
   69|  52.0M|  simdutf_really_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IbEEEC2EDv2_x:
   16|  52.0M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IbEEEcvRKDv2_xEv:
   18|  52.0M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEeoES5_:
   48|  7.97M|  simdutf_really_inline Child operator^(const Child other) const {
   49|  7.97M|    return _mm_xor_si128(*this, other);
   50|  7.97M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2EPKh:
  190|  1.02M|  simdutf_really_inline simd8(const uint8_t *values) : simd8(load(values)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE4loadEPKh:
  106|  23.2M|  static simdutf_really_inline simd8<T> load(const T values[16]) {
  107|  23.2M|    return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
  108|  23.2M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE7gt_bitsES4_:
  212|  1.02M|  gt_bits(const simd8<uint8_t> other) const {
  213|  1.02M|    return this->saturating_sub(other);
  214|  1.02M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEC2EDv2_x:
   16|   116M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEcvRKDv2_xEv:
   18|   157M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IbE10to_bitmaskEv:
   95|  14.2M|  simdutf_really_inline int to_bitmask() const {
   96|  14.2M|    return _mm_movemask_epi8(*this);
   97|  14.2M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE21any_bits_set_anywhereEv:
  235|  2.53M|  simdutf_really_inline bool any_bits_set_anywhere() const {
  236|  2.53M|    return !bits_not_set_anywhere();
  237|  2.53M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE21bits_not_set_anywhereEv:
  232|  2.53M|  simdutf_really_inline bool bits_not_set_anywhere() const {
  233|  2.53M|    return _mm_testz_si128(*this, *this);
  234|  2.53M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IhE8is_asciiEv:
  295|  3.13k|  simdutf_really_inline bool is_ascii() const {
  296|  3.13k|    return this->reduce_or().is_ascii();
  297|  3.13k|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IhE13gteq_unsignedEh:
  352|  2.22k|  simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const {
  353|  2.22k|    const simd8<uint8_t> mask = simd8<uint8_t>::splat(m);
  354|  2.22k|    return simd8x64<bool>(simd8<uint8_t>(__m128i(this->chunks[0])) >= mask,
  355|  2.22k|                          simd8<uint8_t>(__m128i(this->chunks[1])) >= mask,
  356|  2.22k|                          simd8<uint8_t>(__m128i(this->chunks[2])) >= mask,
  357|  2.22k|                          simd8<uint8_t>(__m128i(this->chunks[3])) >= mask)
  358|  2.22k|        .to_bitmask();
  359|  2.22k|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEgeES4_:
  217|  7.23M|  operator>=(const simd8<uint8_t> other) const {
  218|  7.23M|    return other.min_val(*this) == other;
  219|  7.23M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE7min_valES4_:
  207|  7.23M|  min_val(const simd8<uint8_t> other) const {
  208|  7.23M|    return _mm_min_epu8(*this, other);
  209|  7.23M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IbEC2ENS2_5simd8IbEES6_S6_S6_:
  268|  2.20M|      : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IbE10to_bitmaskEv:
  318|  2.20M|  simdutf_really_inline uint64_t to_bitmask() const {
  319|  2.20M|    uint64_t r0 = uint32_t(this->chunks[0].to_bitmask());
  320|  2.20M|    uint64_t r1 = this->chunks[1].to_bitmask();
  321|  2.20M|    uint64_t r2 = this->chunks[2].to_bitmask();
  322|  2.20M|    uint64_t r3 = this->chunks[3].to_bitmask();
  323|  2.20M|    return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
  324|  2.20M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaEC2EPKa:
  270|  6.89M|      : chunks{simd8<T>::load(ptr),
  271|  6.89M|               simd8<T>::load(ptr + sizeof(simd8<T>) / sizeof(T)),
  272|  6.89M|               simd8<T>::load(ptr + 2 * sizeof(simd8<T>) / sizeof(T)),
  273|  6.89M|               simd8<T>::load(ptr + 3 * sizeof(simd8<T>) / sizeof(T))} {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIaE4loadEPKa:
  106|  50.1M|  static simdutf_really_inline simd8<T> load(const T values[16]) {
  107|  50.1M|    return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
  108|  50.1M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IaEC2EDv2_x:
  163|  92.8M|      : base8_numeric<int8_t>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIaEC2EDv2_x:
  120|  92.8M|      : base8<T>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5base8IaNS2_5simd8IbEEEC2EDv2_x:
   69|  92.8M|  simdutf_really_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IaEEEC2EDv2_x:
   16|  92.8M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE8is_asciiEv:
  295|  5.94M|  simdutf_really_inline bool is_ascii() const {
  296|  5.94M|    return this->reduce_or().is_ascii();
  297|  5.94M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE9reduce_orEv:
  290|  5.94M|  simdutf_really_inline simd8<T> reduce_or() const {
  291|  5.94M|    return (this->chunks[0] | this->chunks[1]) |
  292|  5.94M|           (this->chunks[2] | this->chunks[3]);
  293|  5.94M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IaEEEorES5_:
   42|  17.8M|  simdutf_really_inline Child operator|(const Child other) const {
   43|  17.8M|    return _mm_or_si128(*this, other);
   44|  17.8M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IaEEEcvRKDv2_xEv:
   18|   149M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IaE8is_asciiEv:
  168|  5.94M|  simdutf_really_inline bool is_ascii() const {
  169|  5.94M|    return _mm_movemask_epi8(*this) == 0;
  170|  5.94M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE5storeEPa:
  275|   847k|  simdutf_really_inline void store(T *ptr) const {
  276|   847k|    this->chunks[0].store(ptr + sizeof(simd8<T>) * 0 / sizeof(T));
  277|   847k|    this->chunks[1].store(ptr + sizeof(simd8<T>) * 1 / sizeof(T));
  278|   847k|    this->chunks[2].store(ptr + sizeof(simd8<T>) * 2 / sizeof(T));
  279|   847k|    this->chunks[3].store(ptr + sizeof(simd8<T>) * 3 / sizeof(T));
  280|   847k|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIaE5storeEPa:
  123|  3.39M|  simdutf_really_inline void store(T dst[16]) const {
  124|  3.39M|    return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this);
  125|  3.39M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IaEcvNS3_IhEEEv:
  251|  7.03M|simdutf_really_inline simd8<int8_t>::operator simd8<uint8_t>() const {
  252|  7.03M|  return this->value;
  253|  7.03M|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE2ltEa:
  326|  1.25M|  simdutf_really_inline uint64_t lt(const T m) const {
  327|  1.25M|    const simd8<T> mask = simd8<T>::splat(m);
  328|  1.25M|    return simd8x64<bool>(this->chunks[0] < mask, this->chunks[1] < mask,
  329|  1.25M|                          this->chunks[2] < mask, this->chunks[3] < mask)
  330|  1.25M|        .to_bitmask();
  331|  1.25M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIaE5splatEa:
  102|  24.8M|  static simdutf_really_inline simd8<T> splat(T _value) {
  103|  24.8M|    return _mm_set1_epi8(_value);
  104|  24.8M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IaEltES4_:
  176|  5.03M|  simdutf_really_inline simd8<bool> operator<(const simd8<int8_t> other) const {
  177|  5.03M|    return _mm_cmpgt_epi8(other, *this);
  178|  5.03M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE20store_ascii_as_utf32EPDi:
  311|  1.38M|  simdutf_really_inline void store_ascii_as_utf32(char32_t *ptr) const {
  312|  1.38M|    this->chunks[0].store_ascii_as_utf32(ptr + sizeof(simd8<T>) * 0);
  313|  1.38M|    this->chunks[1].store_ascii_as_utf32(ptr + sizeof(simd8<T>) * 1);
  314|  1.38M|    this->chunks[2].store_ascii_as_utf32(ptr + sizeof(simd8<T>) * 2);
  315|  1.38M|    this->chunks[3].store_ascii_as_utf32(ptr + sizeof(simd8<T>) * 3);
  316|  1.38M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IaEEE20store_ascii_as_utf32EPDi:
   32|  5.52M|  simdutf_really_inline void store_ascii_as_utf32(char32_t *p) const {
   33|  5.52M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p), _mm_cvtepu8_epi32(*this));
   34|  5.52M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p + 4),
   35|  5.52M|                     _mm_cvtepu8_epi32(_mm_srli_si128(*this, 4)));
   36|  5.52M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p + 8),
   37|  5.52M|                     _mm_cvtepu8_epi32(_mm_srli_si128(*this, 8)));
   38|  5.52M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p + 12),
   39|       |                     _mm_cvtepu8_epi32(_mm_srli_si128(*this, 12)));
   40|  5.52M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE4zeroEv:
  105|   241k|  static simdutf_really_inline simd8<T> zero() { return _mm_setzero_si128(); }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IaEgtES4_:
  173|  26.3M|  simdutf_really_inline simd8<bool> operator>(const simd8<int8_t> other) const {
  174|  26.3M|    return _mm_cmpgt_epi8(*this, other);
  175|  26.3M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IaEC2Ea:
  165|  22.6M|  simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhEmIENS2_5simd8IhEE:
  134|  29.8M|  simdutf_really_inline simd8<T> &operator-=(const simd8<T> other) {
  135|  29.8M|    *this = *this - other;
  136|  29.8M|    return *static_cast<simd8<T> *>(this);
  137|  29.8M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhEmiENS2_5simd8IhEE:
  131|  29.8M|  simdutf_really_inline simd8<T> operator-(const simd8<T> other) const {
  132|  29.8M|    return _mm_sub_epi8(*this, other);
  133|  29.8M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd10sum_8bytesENS2_5simd8IhEE:
  366|   117k|simdutf_really_inline simd64<uint64_t> sum_8bytes(const simd8<uint8_t> v) {
  367|   117k|  return _mm_sad_epu8(v.value, simd8<uint8_t>::zero());
  368|   117k|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE9sum_bytesEv:
  245|  5.09k|  simdutf_really_inline uint64_t sum_bytes() const {
  246|  5.09k|    const auto tmp = _mm_sad_epu8(value, _mm_setzero_si128());
  247|  5.09k|    return _mm_extract_epi64(tmp, 0) + _mm_extract_epi64(tmp, 1);
  248|  5.09k|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE2gtEa:
  333|   944k|  simdutf_really_inline uint64_t gt(const T m) const {
  334|   944k|    const simd8<T> mask = simd8<T>::splat(m);
  335|   944k|    return simd8x64<bool>(this->chunks[0] > mask, this->chunks[1] > mask,
  336|   944k|                          this->chunks[2] > mask, this->chunks[3] > mask)
  337|   944k|        .to_bitmask();
  338|   944k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEC2EDv2_x:
   16|  29.2M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEcvRKDv2_xEv:
   18|  29.2M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE5splatEh:
  102|  56.5M|  static simdutf_really_inline simd8<T> splat(T _value) {
  103|  56.5M|    return _mm_set1_epi8(_value);
  104|  56.5M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEcvRKDv2_xEv:
   18|   463M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdeqENS2_5simd8IhEES4_:
   72|  12.6M|                                               const simd8<T> rhs) {
   73|  12.6M|    return _mm_cmpeq_epi8(lhs, rhs);
   74|  12.6M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IhEC2EPKh:
  270|  5.54M|      : chunks{simd8<T>::load(ptr),
  271|  5.54M|               simd8<T>::load(ptr + sizeof(simd8<T>) / sizeof(T)),
  272|  5.54M|               simd8<T>::load(ptr + 2 * sizeof(simd8<T>) / sizeof(T)),
  273|  5.54M|               simd8<T>::load(ptr + 3 * sizeof(simd8<T>) / sizeof(T))} {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEanES5_:
   45|  53.9M|  simdutf_really_inline Child operator&(const Child other) const {
   46|  53.9M|    return _mm_and_si128(*this, other);
   47|  53.9M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEanES5_:
   45|  17.4M|  simdutf_really_inline Child operator&(const Child other) const {
   46|  17.4M|    return _mm_and_si128(*this, other);
   47|  17.4M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE20store_ascii_as_utf16ILNS_10endiannessE0EEEvPDs:
  300|  1.14M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const {
  301|  1.14M|    this->chunks[0].template store_ascii_as_utf16<endian>(ptr +
  302|  1.14M|                                                          sizeof(simd8<T>) * 0);
  303|  1.14M|    this->chunks[1].template store_ascii_as_utf16<endian>(ptr +
  304|  1.14M|                                                          sizeof(simd8<T>) * 1);
  305|  1.14M|    this->chunks[2].template store_ascii_as_utf16<endian>(ptr +
  306|  1.14M|                                                          sizeof(simd8<T>) * 2);
  307|  1.14M|    this->chunks[3].template store_ascii_as_utf16<endian>(ptr +
  308|  1.14M|                                                          sizeof(simd8<T>) * 3);
  309|  1.14M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IaEEE20store_ascii_as_utf16ILNS_10endiannessE0EEEvPDs:
   20|  4.57M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *p) const {
   21|  4.57M|    __m128i first = _mm_cvtepu8_epi16(*this);
   22|  4.57M|    __m128i second = _mm_cvtepu8_epi16(_mm_srli_si128(*this, 8));
   23|  4.57M|    if (big_endian) {
  ------------------
  |  Branch (23:9): [Folded, False: 4.57M]
  ------------------
   24|      0|      const __m128i swap =
   25|      0|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   26|      0|      first = _mm_shuffle_epi8(first, swap);
   27|      0|      second = _mm_shuffle_epi8(second, swap);
   28|      0|    }
   29|  4.57M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p), first);
   30|  4.57M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p + 8), second);
   31|  4.57M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE20store_ascii_as_utf16ILNS_10endiannessE1EEEvPDs:
  300|  1.31M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const {
  301|  1.31M|    this->chunks[0].template store_ascii_as_utf16<endian>(ptr +
  302|  1.31M|                                                          sizeof(simd8<T>) * 0);
  303|  1.31M|    this->chunks[1].template store_ascii_as_utf16<endian>(ptr +
  304|  1.31M|                                                          sizeof(simd8<T>) * 1);
  305|  1.31M|    this->chunks[2].template store_ascii_as_utf16<endian>(ptr +
  306|  1.31M|                                                          sizeof(simd8<T>) * 2);
  307|  1.31M|    this->chunks[3].template store_ascii_as_utf16<endian>(ptr +
  308|  1.31M|                                                          sizeof(simd8<T>) * 3);
  309|  1.31M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IaEEE20store_ascii_as_utf16ILNS_10endiannessE1EEEvPDs:
   20|  5.27M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *p) const {
   21|  5.27M|    __m128i first = _mm_cvtepu8_epi16(*this);
   22|  5.27M|    __m128i second = _mm_cvtepu8_epi16(_mm_srli_si128(*this, 8));
   23|  5.27M|    if (big_endian) {
  ------------------
  |  Branch (23:9): [True: 5.27M, Folded]
  ------------------
   24|  5.27M|      const __m128i swap =
   25|  5.27M|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   26|  5.27M|      first = _mm_shuffle_epi8(first, swap);
   27|  5.27M|      second = _mm_shuffle_epi8(second, swap);
   28|  5.27M|    }
   29|  5.27M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p), first);
   30|  5.27M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p + 8), second);
   31|  5.27M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd12as_vector_u8ENS2_6simd32IjEE:
  370|  10.8k|simdutf_really_inline simd8<uint8_t> as_vector_u8(const simd32<uint32_t> v) {
  371|  10.8k|  return simd8<uint8_t>(v.value);
  372|  10.8k|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEorES5_:
   42|  8.37M|  simdutf_really_inline Child operator|(const Child other) const {
   43|  8.37M|    return _mm_or_si128(*this, other);
   44|  8.37M|  }

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd16ItEC2EDv2_x:
   79|   116M|      : base16_numeric<uint16_t>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItEC2EDv2_x:
   54|   116M|      : base16<T>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6base16ItNS2_6simd16IbEEEC2EDv2_x:
    8|   116M|      : base<simd16<T>>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd16ItE4packERKS4_S6_:
  130|  5.35M|                                                   const simd16<uint16_t> &v1) {
  131|  5.35M|    return _mm_packus_epi16(v0, v1);
  132|  5.35M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd9simd16x32ItEC2EPKt:
  164|  2.09M|      : chunks{simd16<T>::load(ptr),
  165|  2.09M|               simd16<T>::load(ptr + sizeof(simd16<T>) / sizeof(T)),
  166|  2.09M|               simd16<T>::load(ptr + 2 * sizeof(simd16<T>) / sizeof(T)),
  167|  2.09M|               simd16<T>::load(ptr + 3 * sizeof(simd16<T>) / sizeof(T))} {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItE4loadEPKt:
   47|  23.1M|  static simdutf_really_inline simd16<T> load(const T values[8]) {
   48|  23.1M|    return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
   49|  23.1M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd9simd16x32ItE10swap_bytesEv:
  200|  1.13M|  simdutf_really_inline void swap_bytes() {
  201|  1.13M|    this->chunks[0] = this->chunks[0].swap_bytes();
  202|  1.13M|    this->chunks[1] = this->chunks[1].swap_bytes();
  203|  1.13M|    this->chunks[2] = this->chunks[2].swap_bytes();
  204|  1.13M|    this->chunks[3] = this->chunks[3].swap_bytes();
  205|  1.13M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd16ItE10swap_bytesEv:
  121|  6.43M|  simdutf_really_inline simd16<uint16_t> swap_bytes() const {
  122|  6.43M|    const __m128i swap =
  123|  6.43M|        _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  124|  6.43M|    return _mm_shuffle_epi8(*this, swap);
  125|  6.43M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItE5splatEt:
   41|  23.1M|  static simdutf_really_inline simd16<T> splat(T _value) {
   42|  23.1M|    return _mm_set1_epi16(_value);
   43|  23.1M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdeqENS2_6simd16ItEES4_:
   11|  20.8M|                                               const simd16<T> rhs) {
   12|  20.8M|    return _mm_cmpeq_epi16(lhs, rhs);
   13|  20.8M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd16IbEC2EDv2_x:
   28|  29.2M|  simdutf_really_inline simd16(const __m128i _value) : base16<bool>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6base16IbNS2_6simd16IbEEEC2EDv2_x:
    8|  29.2M|      : base<simd16<T>>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd9simd16x32IbEC2ENS2_6simd16IbEES6_S6_S6_:
  162|  2.09M|      : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd9simd16x32IbE10to_bitmaskEv:
  192|  2.09M|  simdutf_really_inline uint64_t to_bitmask() const {
  193|  2.09M|    uint64_t r0 = uint32_t(this->chunks[0].to_bitmask());
  194|  2.09M|    uint64_t r1 = this->chunks[1].to_bitmask();
  195|  2.09M|    uint64_t r2 = this->chunks[2].to_bitmask();
  196|  2.09M|    uint64_t r3 = this->chunks[3].to_bitmask();
  197|  2.09M|    return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
  198|  2.09M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd16IbE10to_bitmaskEv:
   33|  8.37M|  simdutf_really_inline int to_bitmask() const {
   34|  8.37M|    return _mm_movemask_epi8(*this);
   35|  8.37M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd16ItEgeES4_:
  104|  8.37M|  operator>=(const simd16<uint16_t> other) const {
  105|  8.37M|    return other.min_val(*this) == other;
  106|  8.37M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd16ItE7min_valES4_:
   95|  8.37M|  min_val(const simd16<uint16_t> other) const {
   96|  8.37M|    return _mm_min_epu16(*this, other);
   97|  8.37M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd16ItE3shrILi8EEES4_v:
  116|  5.43M|  template <int N> simdutf_really_inline simd16<uint16_t> shr() const {
  117|  5.43M|    return simd16<uint16_t>(_mm_srli_epi16(*this, N));
  118|  5.43M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd16ItEleES4_:
  100|  8.37M|  operator<=(const simd16<uint16_t> other) const {
  101|  8.37M|    return other.max_val(*this) == other;
  102|  8.37M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd16ItE7max_valES4_:
   90|  8.37M|  max_val(const simd16<uint16_t> other) const {
   91|  8.37M|    return _mm_max_epu16(*this, other);
   92|  8.37M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd16ItEC2EPKDs:
   86|  10.7M|      : simd16(load(reinterpret_cast<const uint16_t *>(values))) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd16ItEC2Et:
   82|  18.9M|  simdutf_really_inline simd16(uint16_t _value) : simd16(splat(_value)) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd9simd16x32ItE12not_in_rangeEtt:
  228|  2.09M|  simdutf_really_inline uint64_t not_in_range(const T low, const T high) const {
  229|  2.09M|    const simd16<T> mask_low = simd16<T>::splat(static_cast<T>(low - 1));
  230|  2.09M|    const simd16<T> mask_high = simd16<T>::splat(static_cast<T>(high + 1));
  231|  2.09M|    return simd16x32<bool>(
  232|  2.09M|               (this->chunks[0] >= mask_high) | (this->chunks[0] <= mask_low),
  233|  2.09M|               (this->chunks[1] >= mask_high) | (this->chunks[1] <= mask_low),
  234|  2.09M|               (this->chunks[2] >= mask_high) | (this->chunks[2] <= mask_low),
  235|  2.09M|               (this->chunks[3] >= mask_high) | (this->chunks[3] <= mask_low))
  236|  2.09M|        .to_bitmask();
  237|  2.09M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItE4zeroEv:
   45|  1.61k|  static simdutf_really_inline simd16<T> zero() { return _mm_setzero_si128(); }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd3minENS2_6simd16ItEES4_:
  240|  8.13M|simd16<uint16_t> min(const simd16<uint16_t> a, simd16<uint16_t> b) {
  241|  8.13M|  return _mm_min_epu16(a.value, b.value);
  242|  8.13M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItEpLENS2_6simd16ItEE:
   68|  12.2M|  simdutf_really_inline simd16<T> &operator+=(const simd16<T> other) {
   69|  12.2M|    *this = *this + other;
   70|  12.2M|    return *static_cast<simd16<T> *>(this);
   71|  12.2M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd14base16_numericItEplENS2_6simd16ItEE:
   65|  12.2M|  simdutf_really_inline simd16<T> operator+(const simd16<T> other) const {
   66|  12.2M|    return _mm_add_epi16(*this, other);
   67|  12.2M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd16ItE3sumEv:
  134|  1.61k|  simdutf_really_inline uint64_t sum() const {
  135|  1.61k|    const auto lo_u16 = _mm_and_si128(value, _mm_set1_epi32(0x0000ffff));
  136|  1.61k|    const auto hi_u16 = _mm_srli_epi32(value, 16);
  137|  1.61k|    const auto sum_u32 = _mm_add_epi32(lo_u16, hi_u16);
  138|       |
  139|  1.61k|    const auto lo_u32 = _mm_and_si128(sum_u32, _mm_set1_epi64x(0xffffffff));
  140|  1.61k|    const auto hi_u32 = _mm_srli_epi64(sum_u32, 32);
  141|  1.61k|    const auto sum_u64 = _mm_add_epi64(lo_u32, hi_u32);
  142|       |
  143|  1.61k|    return uint64_t(_mm_extract_epi64(sum_u64, 0)) +
  144|       |           uint64_t(_mm_extract_epi64(sum_u64, 1));
  145|  1.61k|  }

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd32IjE5splatEj:
   56|  36.1k|  simdutf_really_inline static simd32<uint32_t> splat(uint32_t v) {
   57|  36.1k|    return _mm_set1_epi32(v);
   58|  36.1k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd32IjEC2EDv2_x:
    9|  14.8M|  simdutf_really_inline simd32(const __m128i v) : value(v) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd32IjE4zeroEv:
   52|  6.25k|  simdutf_really_inline static simd32<uint32_t> zero() {
   53|  6.25k|    return _mm_setzero_si128();
   54|  6.25k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd32IjEC2IDiEEPKT_:
   13|  3.45M|      : value(_mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr))) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd32IjE10swap_bytesEv:
   22|  2.71M|  simdutf_really_inline simd32<uint32_t> swap_bytes() const {
   23|  2.71M|    const __m128i shuffle =
   24|  2.71M|        _mm_setr_epi8(3, 2, 1, 0, 7, 6, 5, 4, 8, 9, 10, 11, 15, 14, 13, 12);
   25|       |
   26|  2.71M|    return _mm_shuffle_epi8(value, shuffle);
   27|  2.71M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd3maxENS2_6simd32IjEES4_:
   94|  4.85M|                                           const simd32<uint32_t> b) {
   95|  4.85M|  return _mm_max_epu32(a.value, b.value);
   96|  4.85M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdplENS2_6simd32IjEES4_:
  119|  2.43M|                                                 const simd32<uint32_t> b) {
  120|  2.43M|  return _mm_add_epi32(a.value, b.value);
  121|  2.43M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdgtENS2_6simd32IjEES4_:
  143|  20.8k|                                             const simd32<uint32_t> b) {
  144|  20.8k|  return !(b >= a);
  145|  20.8k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdntENS2_6simd32IbEE:
  138|  20.8k|simdutf_really_inline simd32<bool> operator!(const simd32<bool> v) {
  139|  20.8k|  return _mm_xor_si128(v.value, _mm_set1_epi8(-1));
  140|  20.8k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd32IbEC2EDv2_x:
   69|   935k|  simdutf_really_inline simd32(const __m128i v) : value(v) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdgeENS2_6simd32IjEES4_:
  134|   311k|                                              const simd32<uint32_t> b) {
  135|   311k|  return _mm_cmpeq_epi32(_mm_max_epu32(a.value, b.value), a.value);
  136|   311k|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd32IbE3anyEv:
   71|   300k|  simdutf_really_inline bool any() const {
   72|   300k|    return _mm_movemask_epi8(value) != 0;
   73|   300k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdeqENS2_6simd32IjEES4_:
  129|   290k|                                              const simd32<uint32_t> b) {
  130|   290k|  return _mm_cmpeq_epi32(a.value, b.value);
  131|   290k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdanENS2_6simd32IjEES4_:
  104|  2.49M|                                                 const simd32<uint32_t> b) {
  105|  2.49M|  return _mm_and_si128(a.value, b.value);
  106|  2.49M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdorIbEENS2_6simd32IT_EES6_S6_:
   84|   301k|                                          const simd32<T> b) {
   85|   301k|  return _mm_or_si128(a.value, b.value);
   86|   301k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd3minENS2_6simd32IjEES4_:
   89|  2.20M|                                           const simd32<uint32_t> b) {
   90|  2.20M|  return _mm_min_epu32(a.value, b.value);
   91|  2.20M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd32IjEpLES4_:
   46|  2.20M|  simdutf_really_inline simd32 &operator+=(const simd32 other) {
   47|  2.20M|    value = _mm_add_epi32(value, other.value);
   48|  2.20M|    return *this;
   49|  2.20M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd32IjE3sumEv:
   15|  1.00k|  simdutf_really_inline uint64_t sum() const {
   16|  1.00k|    return uint64_t(_mm_extract_epi32(value, 0)) +
   17|  1.00k|           uint64_t(_mm_extract_epi32(value, 1)) +
   18|  1.00k|           uint64_t(_mm_extract_epi32(value, 2)) +
   19|       |           uint64_t(_mm_extract_epi32(value, 3));
   20|  1.00k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdeqENS2_6simd32IjEEj:
   99|  10.8k|                                              uint32_t b) {
  100|  10.8k|  return _mm_cmpeq_epi32(a.value, _mm_set1_epi32(b));
  101|  10.8k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdanENS2_6simd32IjEEj:
  109|  32.5k|                                                 uint32_t b) {
  110|  32.5k|  return _mm_and_si128(a.value, _mm_set1_epi32(b));
  111|  32.5k|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd32IbE15to_4bit_bitmaskEv:
   75|  10.8k|  simdutf_really_inline uint8_t to_4bit_bitmask() const {
   76|  10.8k|    return uint8_t(_mm_movemask_ps(_mm_castsi128_ps(value)));
   77|  10.8k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdmiENS2_6simd32IjEEj:
  124|  10.8k|                                                 uint32_t b) {
  125|  10.8k|  return _mm_sub_epi32(a.value, _mm_set1_epi32(b));
  126|  10.8k|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd32IjE3shrILi10EEES4_v:
   29|  10.8k|  template <int N> simdutf_really_inline simd32<uint32_t> shr() const {
   30|  10.8k|    return _mm_srli_epi32(value, N);
   31|  10.8k|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd32IjE3shlILi16EEES4_v:
   33|  10.8k|  template <int N> simdutf_really_inline simd32<uint32_t> shl() const {
   34|  10.8k|    return _mm_slli_epi32(value, N);
   35|  10.8k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdorENS2_6simd32IjEEj:
  114|  10.8k|                                                 uint32_t b) {
  115|  10.8k|  return _mm_or_si128(a.value, _mm_set1_epi32(b));
  116|  10.8k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdorIjEENS2_6simd32IT_EES6_S6_:
   84|  10.8k|                                          const simd32<T> b) {
   85|  10.8k|  return _mm_or_si128(a.value, b.value);
   86|  10.8k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6selectENS2_6simd32IbEENS3_IjEES5_:
  149|  10.8k|                                              const simd32<uint32_t> v_false) {
  150|  10.8k|  return _mm_blendv_epi8(v_false.value, v_true.value, cond.value);
  151|  10.8k|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd64ImE4zeroEv:
   26|  7.21k|  simdutf_really_inline static simd64<uint64_t> zero() {
   27|  7.21k|    return _mm_setzero_si128();
   28|  7.21k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd64ImEC2EDv2_x:
    9|   124k|  simdutf_really_inline simd64(const __m128i v) : value(v) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd6simd64ImEpLES4_:
   20|   117k|  simdutf_really_inline simd64 &operator+=(const simd64 other) {
   21|   117k|    value = _mm_add_epi64(value, other.value);
   22|   117k|    return *this;
   23|   117k|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd6simd64ImE3sumEv:
   15|  7.21k|  simdutf_really_inline uint64_t sum() const {
   16|  7.21k|    return _mm_extract_epi64(value, 0) + _mm_extract_epi64(value, 1);
   17|  7.21k|  }

_ZNK7simdutf8westmere14implementation13validate_utf8EPKcm:
  299|  4.20k|implementation::validate_utf8(const char *buf, size_t len) const noexcept {
  300|  4.20k|  return westmere::utf8_validation::generic_validate_utf8(buf, len);
  301|  4.20k|}
_ZNK7simdutf8westmere14implementation25validate_utf8_with_errorsEPKcm:
  306|  4.20k|    const char *buf, size_t len) const noexcept {
  307|  4.20k|  return westmere::utf8_validation::generic_validate_utf8_with_errors(buf, len);
  308|  4.20k|}
_ZNK7simdutf8westmere14implementation16validate_utf16leEPKDsm:
  345|  1.43k|                                 size_t len) const noexcept {
  346|  1.43k|  if (simdutf_unlikely(len == 0)) {
  ------------------
  |  |   93|  1.43k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (93:33): [True: 17, False: 1.41k]
  |  |  ------------------
  ------------------
  347|       |    // empty input is valid UTF-16. protect the implementation from
  348|       |    // handling nullptr
  349|     17|    return true;
  350|     17|  }
  351|  1.41k|  const auto res =
  352|  1.41k|      westmere::utf16::validate_utf16_with_errors<endianness::LITTLE>(buf, len);
  353|  1.41k|  if (res.is_err()) {
  ------------------
  |  Branch (353:7): [True: 142, False: 1.27k]
  ------------------
  354|    142|    return false;
  355|    142|  }
  356|       |
  357|  1.27k|  if (res.count == len)
  ------------------
  |  Branch (357:7): [True: 0, False: 1.27k]
  ------------------
  358|      0|    return true;
  359|       |
  360|  1.27k|  return scalar::utf16::validate<endianness::LITTLE>(buf + res.count,
  361|  1.27k|                                                     len - res.count);
  362|  1.27k|}
_ZNK7simdutf8westmere14implementation16validate_utf16beEPKDsm:
  368|  1.32k|                                 size_t len) const noexcept {
  369|  1.32k|  if (simdutf_unlikely(len == 0)) {
  ------------------
  |  |   93|  1.32k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (93:33): [True: 16, False: 1.30k]
  |  |  ------------------
  ------------------
  370|       |    // empty input is valid UTF-16. protect the implementation from
  371|       |    // handling nullptr
  372|     16|    return true;
  373|     16|  }
  374|  1.30k|  const auto res =
  375|  1.30k|      westmere::utf16::validate_utf16_with_errors<endianness::BIG>(buf, len);
  376|  1.30k|  if (res.is_err()) {
  ------------------
  |  Branch (376:7): [True: 127, False: 1.17k]
  ------------------
  377|    127|    return false;
  378|    127|  }
  379|       |
  380|  1.17k|  if (res.count == len)
  ------------------
  |  Branch (380:7): [True: 0, False: 1.17k]
  ------------------
  381|      0|    return true;
  382|       |
  383|  1.17k|  return scalar::utf16::validate<endianness::BIG>(buf + res.count,
  384|  1.17k|                                                  len - res.count);
  385|  1.17k|}
_ZNK7simdutf8westmere14implementation28validate_utf16le_with_errorsEPKDsm:
  388|  1.43k|    const char16_t *buf, size_t len) const noexcept {
  389|  1.43k|  const result res =
  390|  1.43k|      westmere::utf16::validate_utf16_with_errors<endianness::LITTLE>(buf, len);
  391|  1.43k|  if (res.count != len) {
  ------------------
  |  Branch (391:7): [True: 1.41k, False: 17]
  ------------------
  392|  1.41k|    const result scalar_res =
  393|  1.41k|        scalar::utf16::validate_with_errors<endianness::LITTLE>(
  394|  1.41k|            buf + res.count, len - res.count);
  395|  1.41k|    return result(scalar_res.error, res.count + scalar_res.count);
  396|  1.41k|  } else {
  397|     17|    return res;
  398|     17|  }
  399|  1.43k|}
_ZNK7simdutf8westmere14implementation28validate_utf16be_with_errorsEPKDsm:
  402|  1.32k|    const char16_t *buf, size_t len) const noexcept {
  403|  1.32k|  const result res =
  404|  1.32k|      westmere::utf16::validate_utf16_with_errors<endianness::BIG>(buf, len);
  405|  1.32k|  if (res.count != len) {
  ------------------
  |  Branch (405:7): [True: 1.30k, False: 16]
  ------------------
  406|  1.30k|    result scalar_res = scalar::utf16::validate_with_errors<endianness::BIG>(
  407|  1.30k|        buf + res.count, len - res.count);
  408|  1.30k|    return result(scalar_res.error, res.count + scalar_res.count);
  409|  1.30k|  } else {
  410|     16|    return res;
  411|     16|  }
  412|  1.32k|}
_ZNK7simdutf8westmere14implementation14validate_utf32EPKDim:
  427|  2.64k|implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept {
  428|  2.64k|  return utf32::validate(buf, len);
  429|  2.64k|}
_ZNK7simdutf8westmere14implementation26validate_utf32_with_errorsEPKDim:
  434|  2.64k|    const char32_t *buf, size_t len) const noexcept {
  435|  2.64k|  return utf32::validate_with_errors(buf, len);
  436|  2.64k|}
_ZNK7simdutf8westmere14implementation22convert_latin1_to_utf8EPKcmPc:
  441|    560|    const char *buf, size_t len, char *utf8_output) const noexcept {
  442|       |
  443|    560|  std::pair<const char *, char *> ret =
  444|    560|      sse_convert_latin1_to_utf8(buf, len, utf8_output);
  445|    560|  size_t converted_chars = ret.second - utf8_output;
  446|       |
  447|    560|  if (ret.first != buf + len) {
  ------------------
  |  Branch (447:7): [True: 460, False: 100]
  ------------------
  448|    460|    const size_t scalar_converted_chars = scalar::latin1_to_utf8::convert(
  449|    460|        ret.first, len - (ret.first - buf), ret.second);
  450|    460|    converted_chars += scalar_converted_chars;
  451|    460|  }
  452|       |
  453|    560|  return converted_chars;
  454|    560|}
_ZNK7simdutf8westmere14implementation25convert_latin1_to_utf16leEPKcmPDs:
  459|     76|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  460|     76|  std::pair<const char *, char16_t *> ret =
  461|     76|      sse_convert_latin1_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
  462|     76|  if (ret.first == nullptr) {
  ------------------
  |  Branch (462:7): [True: 4, False: 72]
  ------------------
  463|      4|    return 0;
  464|      4|  }
  465|     72|  size_t converted_chars = ret.second - utf16_output;
  466|     72|  if (ret.first != buf + len) {
  ------------------
  |  Branch (466:7): [True: 58, False: 14]
  ------------------
  467|     58|    const size_t scalar_converted_chars =
  468|     58|        scalar::latin1_to_utf16::convert<endianness::LITTLE>(
  469|     58|            ret.first, len - (ret.first - buf), ret.second);
  470|     58|    if (scalar_converted_chars == 0) {
  ------------------
  |  Branch (470:9): [True: 0, False: 58]
  ------------------
  471|      0|      return 0;
  472|      0|    }
  473|     58|    converted_chars += scalar_converted_chars;
  474|     58|  }
  475|     72|  return converted_chars;
  476|     72|}
_ZNK7simdutf8westmere14implementation25convert_latin1_to_utf16beEPKcmPDs:
  479|    110|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  480|    110|  std::pair<const char *, char16_t *> ret =
  481|    110|      sse_convert_latin1_to_utf16<endianness::BIG>(buf, len, utf16_output);
  482|    110|  if (ret.first == nullptr) {
  ------------------
  |  Branch (482:7): [True: 4, False: 106]
  ------------------
  483|      4|    return 0;
  484|      4|  }
  485|    106|  size_t converted_chars = ret.second - utf16_output;
  486|    106|  if (ret.first != buf + len) {
  ------------------
  |  Branch (486:7): [True: 88, False: 18]
  ------------------
  487|     88|    const size_t scalar_converted_chars =
  488|     88|        scalar::latin1_to_utf16::convert<endianness::BIG>(
  489|     88|            ret.first, len - (ret.first - buf), ret.second);
  490|     88|    if (scalar_converted_chars == 0) {
  ------------------
  |  Branch (490:9): [True: 0, False: 88]
  ------------------
  491|      0|      return 0;
  492|      0|    }
  493|     88|    converted_chars += scalar_converted_chars;
  494|     88|  }
  495|    106|  return converted_chars;
  496|    106|}
_ZNK7simdutf8westmere14implementation23convert_latin1_to_utf32EPKcmPDi:
  501|     96|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  502|     96|  std::pair<const char *, char32_t *> ret =
  503|     96|      sse_convert_latin1_to_utf32(buf, len, utf32_output);
  504|     96|  if (ret.first == nullptr) {
  ------------------
  |  Branch (504:7): [True: 4, False: 92]
  ------------------
  505|      4|    return 0;
  506|      4|  }
  507|     92|  size_t converted_chars = ret.second - utf32_output;
  508|     92|  if (ret.first != buf + len) {
  ------------------
  |  Branch (508:7): [True: 70, False: 22]
  ------------------
  509|     70|    const size_t scalar_converted_chars = scalar::latin1_to_utf32::convert(
  510|     70|        ret.first, len - (ret.first - buf), ret.second);
  511|     70|    if (scalar_converted_chars == 0) {
  ------------------
  |  Branch (511:9): [True: 0, False: 70]
  ------------------
  512|      0|      return 0;
  513|      0|    }
  514|     70|    converted_chars += scalar_converted_chars;
  515|     70|  }
  516|     92|  return converted_chars;
  517|     92|}
_ZNK7simdutf8westmere14implementation22convert_utf8_to_latin1EPKcmPc:
  522|    710|    const char *buf, size_t len, char *latin1_output) const noexcept {
  523|    710|  utf8_to_latin1::validating_transcoder converter;
  524|    710|  return converter.convert(buf, len, latin1_output);
  525|    710|}
_ZNK7simdutf8westmere14implementation34convert_utf8_to_latin1_with_errorsEPKcmPc:
  528|    730|    const char *buf, size_t len, char *latin1_output) const noexcept {
  529|    730|  utf8_to_latin1::validating_transcoder converter;
  530|    730|  return converter.convert_with_errors(buf, len, latin1_output);
  531|    730|}
_ZNK7simdutf8westmere14implementation23convert_utf8_to_utf16leEPKcmPDs:
  541|    950|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  542|    950|  utf8_to_utf16::validating_transcoder converter;
  543|    950|  return converter.convert<endianness::LITTLE>(buf, len, utf16_output);
  544|    950|}
_ZNK7simdutf8westmere14implementation23convert_utf8_to_utf16beEPKcmPDs:
  547|  1.00k|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  548|  1.00k|  utf8_to_utf16::validating_transcoder converter;
  549|  1.00k|  return converter.convert<endianness::BIG>(buf, len, utf16_output);
  550|  1.00k|}
_ZNK7simdutf8westmere14implementation35convert_utf8_to_utf16le_with_errorsEPKcmPDs:
  553|    746|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  554|    746|  utf8_to_utf16::validating_transcoder converter;
  555|    746|  return converter.convert_with_errors<endianness::LITTLE>(buf, len,
  556|    746|                                                           utf16_output);
  557|    746|}
_ZNK7simdutf8westmere14implementation35convert_utf8_to_utf16be_with_errorsEPKcmPDs:
  560|    934|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  561|    934|  utf8_to_utf16::validating_transcoder converter;
  562|    934|  return converter.convert_with_errors<endianness::BIG>(buf, len, utf16_output);
  563|    934|}
_ZNK7simdutf8westmere14implementation29convert_valid_utf8_to_utf16leEPKcmPDs:
  566|    380|    const char *input, size_t size, char16_t *utf16_output) const noexcept {
  567|    380|  return utf8_to_utf16::convert_valid<endianness::LITTLE>(input, size,
  568|    380|                                                          utf16_output);
  569|    380|}
_ZNK7simdutf8westmere14implementation29convert_valid_utf8_to_utf16beEPKcmPDs:
  572|    518|    const char *input, size_t size, char16_t *utf16_output) const noexcept {
  573|    518|  return utf8_to_utf16::convert_valid<endianness::BIG>(input, size,
  574|    518|                                                       utf16_output);
  575|    518|}
_ZNK7simdutf8westmere14implementation21convert_utf8_to_utf32EPKcmPDi:
  580|    956|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  581|    956|  utf8_to_utf32::validating_transcoder converter;
  582|    956|  return converter.convert(buf, len, utf32_output);
  583|    956|}
_ZNK7simdutf8westmere14implementation33convert_utf8_to_utf32_with_errorsEPKcmPDi:
  586|    926|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  587|    926|  utf8_to_utf32::validating_transcoder converter;
  588|    926|  return converter.convert_with_errors(buf, len, utf32_output);
  589|    926|}
_ZNK7simdutf8westmere14implementation27convert_valid_utf8_to_utf32EPKcmPDi:
  592|    506|    const char *input, size_t size, char32_t *utf32_output) const noexcept {
  593|    506|  return utf8_to_utf32::convert_valid(input, size, utf32_output);
  594|    506|}
_ZNK7simdutf8westmere14implementation25convert_utf16le_to_latin1EPKDsmPc:
  599|    120|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  600|    120|  std::pair<const char16_t *, char *> ret =
  601|    120|      sse_convert_utf16_to_latin1<endianness::LITTLE>(buf, len, latin1_output);
  602|    120|  if (ret.first == nullptr) {
  ------------------
  |  Branch (602:7): [True: 42, False: 78]
  ------------------
  603|     42|    return 0;
  604|     42|  }
  605|     78|  size_t saved_bytes = ret.second - latin1_output;
  606|       |
  607|     78|  if (ret.first != buf + len) {
  ------------------
  |  Branch (607:7): [True: 60, False: 18]
  ------------------
  608|     60|    const size_t scalar_saved_bytes =
  609|     60|        scalar::utf16_to_latin1::convert<endianness::LITTLE>(
  610|     60|            ret.first, len - (ret.first - buf), ret.second);
  611|     60|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (611:9): [True: 34, False: 26]
  ------------------
  612|     34|      return 0;
  613|     34|    }
  614|     26|    saved_bytes += scalar_saved_bytes;
  615|     26|  }
  616|     44|  return saved_bytes;
  617|     78|}
_ZNK7simdutf8westmere14implementation25convert_utf16be_to_latin1EPKDsmPc:
  620|     98|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  621|     98|  std::pair<const char16_t *, char *> ret =
  622|     98|      sse_convert_utf16_to_latin1<endianness::BIG>(buf, len, latin1_output);
  623|     98|  if (ret.first == nullptr) {
  ------------------
  |  Branch (623:7): [True: 22, False: 76]
  ------------------
  624|     22|    return 0;
  625|     22|  }
  626|     76|  size_t saved_bytes = ret.second - latin1_output;
  627|       |
  628|     76|  if (ret.first != buf + len) {
  ------------------
  |  Branch (628:7): [True: 58, False: 18]
  ------------------
  629|     58|    const size_t scalar_saved_bytes =
  630|     58|        scalar::utf16_to_latin1::convert<endianness::BIG>(
  631|     58|            ret.first, len - (ret.first - buf), ret.second);
  632|     58|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (632:9): [True: 28, False: 30]
  ------------------
  633|     28|      return 0;
  634|     28|    }
  635|     30|    saved_bytes += scalar_saved_bytes;
  636|     30|  }
  637|     48|  return saved_bytes;
  638|     76|}
_ZNK7simdutf8westmere14implementation37convert_utf16le_to_latin1_with_errorsEPKDsmPc:
  642|    280|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  643|    280|  std::pair<result, char *> ret =
  644|    280|      sse_convert_utf16_to_latin1_with_errors<endianness::LITTLE>(
  645|    280|          buf, len, latin1_output);
  646|    280|  if (ret.first.error) {
  ------------------
  |  Branch (646:7): [True: 140, False: 140]
  ------------------
  647|    140|    return ret.first;
  648|    140|  } // Can return directly since scalar fallback already found correct
  649|       |    // ret.first.count
  650|    140|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (650:7): [True: 96, False: 44]
  ------------------
  651|     96|    result scalar_res =
  652|     96|        scalar::utf16_to_latin1::convert_with_errors<endianness::LITTLE>(
  653|     96|            buf + ret.first.count, len - ret.first.count, ret.second);
  654|     96|    if (scalar_res.error) {
  ------------------
  |  Branch (654:9): [True: 34, False: 62]
  ------------------
  655|     34|      scalar_res.count += ret.first.count;
  656|     34|      return scalar_res;
  657|     62|    } else {
  658|     62|      ret.second += scalar_res.count;
  659|     62|    }
  660|     96|  }
  661|    106|  ret.first.count =
  662|    106|      ret.second -
  663|    106|      latin1_output; // Set count to the number of 8-bit code units written
  664|    106|  return ret.first;
  665|    140|}
_ZNK7simdutf8westmere14implementation37convert_utf16be_to_latin1_with_errorsEPKDsmPc:
  669|    248|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  670|    248|  std::pair<result, char *> ret =
  671|    248|      sse_convert_utf16_to_latin1_with_errors<endianness::BIG>(buf, len,
  672|    248|                                                               latin1_output);
  673|    248|  if (ret.first.error) {
  ------------------
  |  Branch (673:7): [True: 128, False: 120]
  ------------------
  674|    128|    return ret.first;
  675|    128|  } // Can return directly since scalar fallback already found correct
  676|       |    // ret.first.count
  677|    120|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (677:7): [True: 86, False: 34]
  ------------------
  678|     86|    result scalar_res =
  679|     86|        scalar::utf16_to_latin1::convert_with_errors<endianness::BIG>(
  680|     86|            buf + ret.first.count, len - ret.first.count, ret.second);
  681|     86|    if (scalar_res.error) {
  ------------------
  |  Branch (681:9): [True: 34, False: 52]
  ------------------
  682|     34|      scalar_res.count += ret.first.count;
  683|     34|      return scalar_res;
  684|     52|    } else {
  685|     52|      ret.second += scalar_res.count;
  686|     52|    }
  687|     86|  }
  688|     86|  ret.first.count =
  689|     86|      ret.second -
  690|     86|      latin1_output; // Set count to the number of 8-bit code units written
  691|     86|  return ret.first;
  692|    120|}
_ZNK7simdutf8westmere14implementation23convert_utf16le_to_utf8EPKDsmPc:
  709|    874|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  710|    874|  std::pair<const char16_t *, char *> ret =
  711|    874|      sse_convert_utf16_to_utf8<endianness::LITTLE>(buf, len, utf8_output);
  712|    874|  if (ret.first == nullptr) {
  ------------------
  |  Branch (712:7): [True: 124, False: 750]
  ------------------
  713|    124|    return 0;
  714|    124|  }
  715|    750|  size_t saved_bytes = ret.second - utf8_output;
  716|    750|  if (ret.first != buf + len) {
  ------------------
  |  Branch (716:7): [True: 748, False: 2]
  ------------------
  717|    748|    const size_t scalar_saved_bytes =
  718|    748|        scalar::utf16_to_utf8::convert<endianness::LITTLE>(
  719|    748|            ret.first, len - (ret.first - buf), ret.second);
  720|    748|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (720:9): [True: 70, False: 678]
  ------------------
  721|     70|      return 0;
  722|     70|    }
  723|    678|    saved_bytes += scalar_saved_bytes;
  724|    678|  }
  725|    680|  return saved_bytes;
  726|    750|}
_ZNK7simdutf8westmere14implementation23convert_utf16be_to_utf8EPKDsmPc:
  729|    846|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  730|    846|  std::pair<const char16_t *, char *> ret =
  731|    846|      sse_convert_utf16_to_utf8<endianness::BIG>(buf, len, utf8_output);
  732|    846|  if (ret.first == nullptr) {
  ------------------
  |  Branch (732:7): [True: 98, False: 748]
  ------------------
  733|     98|    return 0;
  734|     98|  }
  735|    748|  size_t saved_bytes = ret.second - utf8_output;
  736|    748|  if (ret.first != buf + len) {
  ------------------
  |  Branch (736:7): [True: 746, False: 2]
  ------------------
  737|    746|    const size_t scalar_saved_bytes =
  738|    746|        scalar::utf16_to_utf8::convert<endianness::BIG>(
  739|    746|            ret.first, len - (ret.first - buf), ret.second);
  740|    746|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (740:9): [True: 88, False: 658]
  ------------------
  741|     88|      return 0;
  742|     88|    }
  743|    658|    saved_bytes += scalar_saved_bytes;
  744|    658|  }
  745|    660|  return saved_bytes;
  746|    748|}
_ZNK7simdutf8westmere14implementation35convert_utf16le_to_utf8_with_errorsEPKDsmPc:
  770|    686|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  771|    686|  return convert_utf16_to_utf8_with_details<endianness::LITTLE>(buf, len,
  772|    686|                                                                utf8_output);
  773|    686|}
_ZNK7simdutf8westmere14implementation35convert_utf16be_to_utf8_with_errorsEPKDsmPc:
  776|    556|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  777|    556|  return convert_utf16_to_utf8_with_details<endianness::BIG>(buf, len,
  778|    556|                                                             utf8_output);
  779|    556|}
_ZNK7simdutf8westmere14implementation29convert_valid_utf16le_to_utf8EPKDsmPc:
  782|    402|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  783|    402|  return convert_utf16le_to_utf8(buf, len, utf8_output);
  784|    402|}
_ZNK7simdutf8westmere14implementation29convert_valid_utf16be_to_utf8EPKDsmPc:
  787|    450|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  788|    450|  return convert_utf16be_to_utf8(buf, len, utf8_output);
  789|    450|}
_ZNK7simdutf8westmere14implementation23convert_utf32_to_latin1EPKDimPc:
  794|    186|    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
  795|    186|  std::pair<const char32_t *, char *> ret =
  796|    186|      sse_convert_utf32_to_latin1(buf, len, latin1_output);
  797|    186|  if (ret.first == nullptr) {
  ------------------
  |  Branch (797:7): [True: 40, False: 146]
  ------------------
  798|     40|    return 0;
  799|     40|  }
  800|    146|  size_t saved_bytes = ret.second - latin1_output;
  801|       |  // if (ret.first != buf + len) {
  802|    146|  if (ret.first < buf + len) {
  ------------------
  |  Branch (802:7): [True: 124, False: 22]
  ------------------
  803|    124|    const size_t scalar_saved_bytes = scalar::utf32_to_latin1::convert(
  804|    124|        ret.first, len - (ret.first - buf), ret.second);
  805|    124|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (805:9): [True: 106, False: 18]
  ------------------
  806|    106|      return 0;
  807|    106|    }
  808|     18|    saved_bytes += scalar_saved_bytes;
  809|     18|  }
  810|     40|  return saved_bytes;
  811|    146|}
_ZNK7simdutf8westmere14implementation35convert_utf32_to_latin1_with_errorsEPKDimPc:
  814|    420|    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
  815|       |  // ret.first.count is always the position in the buffer, not the number of
  816|       |  // code units written even if finished
  817|    420|  std::pair<result, char *> ret =
  818|    420|      westmere::sse_convert_utf32_to_latin1_with_errors(buf, len,
  819|    420|                                                        latin1_output);
  820|    420|  if (ret.first.count != len) {
  ------------------
  |  Branch (820:7): [True: 398, False: 22]
  ------------------
  821|    398|    result scalar_res = scalar::utf32_to_latin1::convert_with_errors(
  822|    398|        buf + ret.first.count, len - ret.first.count, ret.second);
  823|    398|    if (scalar_res.error) {
  ------------------
  |  Branch (823:9): [True: 376, False: 22]
  ------------------
  824|    376|      scalar_res.count += ret.first.count;
  825|    376|      return scalar_res;
  826|    376|    } else {
  827|     22|      ret.second += scalar_res.count;
  828|     22|    }
  829|    398|  }
  830|     44|  ret.first.count =
  831|     44|      ret.second -
  832|     44|      latin1_output; // Set count to the number of 8-bit code units written
  833|     44|  return ret.first;
  834|    420|}
_ZNK7simdutf8westmere14implementation21convert_utf32_to_utf8EPKDimPc:
  845|    942|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  846|    942|  std::pair<const char32_t *, char *> ret =
  847|    942|      sse_convert_utf32_to_utf8(buf, len, utf8_output);
  848|    942|  if (ret.first == nullptr) {
  ------------------
  |  Branch (848:7): [True: 300, False: 642]
  ------------------
  849|    300|    return 0;
  850|    300|  }
  851|    642|  size_t saved_bytes = ret.second - utf8_output;
  852|    642|  if (ret.first != buf + len) {
  ------------------
  |  Branch (852:7): [True: 640, False: 2]
  ------------------
  853|    640|    const size_t scalar_saved_bytes = scalar::utf32_to_utf8::convert(
  854|    640|        ret.first, len - (ret.first - buf), ret.second);
  855|    640|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (855:9): [True: 294, False: 346]
  ------------------
  856|    294|      return 0;
  857|    294|    }
  858|    346|    saved_bytes += scalar_saved_bytes;
  859|    346|  }
  860|    348|  return saved_bytes;
  861|    642|}
_ZNK7simdutf8westmere14implementation33convert_utf32_to_utf8_with_errorsEPKDimPc:
  864|    864|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  865|       |  // ret.first.count is always the position in the buffer, not the number of
  866|       |  // code units written even if finished
  867|    864|  std::pair<result, char *> ret =
  868|    864|      westmere::sse_convert_utf32_to_utf8_with_errors(buf, len, utf8_output);
  869|    864|  if (ret.first.count != len) {
  ------------------
  |  Branch (869:7): [True: 860, False: 4]
  ------------------
  870|    860|    result scalar_res = scalar::utf32_to_utf8::convert_with_errors(
  871|    860|        buf + ret.first.count, len - ret.first.count, ret.second);
  872|    860|    if (scalar_res.error) {
  ------------------
  |  Branch (872:9): [True: 656, False: 204]
  ------------------
  873|    656|      scalar_res.count += ret.first.count;
  874|    656|      return scalar_res;
  875|    656|    } else {
  876|    204|      ret.second += scalar_res.count;
  877|    204|    }
  878|    860|  }
  879|    208|  ret.first.count =
  880|    208|      ret.second -
  881|    208|      utf8_output; // Set count to the number of 8-bit code units written
  882|    208|  return ret.first;
  883|    864|}
_ZNK7simdutf8westmere14implementation24convert_utf16le_to_utf32EPKDsmPDi:
  888|    430|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  889|    430|  std::pair<const char16_t *, char32_t *> ret =
  890|    430|      sse_convert_utf16_to_utf32<endianness::LITTLE>(buf, len, utf32_output);
  891|    430|  if (ret.first == nullptr) {
  ------------------
  |  Branch (891:7): [True: 122, False: 308]
  ------------------
  892|    122|    return 0;
  893|    122|  }
  894|    308|  size_t saved_bytes = ret.second - utf32_output;
  895|    308|  if (ret.first != buf + len) {
  ------------------
  |  Branch (895:7): [True: 250, False: 58]
  ------------------
  896|    250|    const size_t scalar_saved_bytes =
  897|    250|        scalar::utf16_to_utf32::convert<endianness::LITTLE>(
  898|    250|            ret.first, len - (ret.first - buf), ret.second);
  899|    250|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (899:9): [True: 66, False: 184]
  ------------------
  900|     66|      return 0;
  901|     66|    }
  902|    184|    saved_bytes += scalar_saved_bytes;
  903|    184|  }
  904|    242|  return saved_bytes;
  905|    308|}
_ZNK7simdutf8westmere14implementation24convert_utf16be_to_utf32EPKDsmPDi:
  908|    458|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  909|    458|  std::pair<const char16_t *, char32_t *> ret =
  910|    458|      sse_convert_utf16_to_utf32<endianness::BIG>(buf, len, utf32_output);
  911|    458|  if (ret.first == nullptr) {
  ------------------
  |  Branch (911:7): [True: 118, False: 340]
  ------------------
  912|    118|    return 0;
  913|    118|  }
  914|    340|  size_t saved_bytes = ret.second - utf32_output;
  915|    340|  if (ret.first != buf + len) {
  ------------------
  |  Branch (915:7): [True: 276, False: 64]
  ------------------
  916|    276|    const size_t scalar_saved_bytes =
  917|    276|        scalar::utf16_to_utf32::convert<endianness::BIG>(
  918|    276|            ret.first, len - (ret.first - buf), ret.second);
  919|    276|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (919:9): [True: 58, False: 218]
  ------------------
  920|     58|      return 0;
  921|     58|    }
  922|    218|    saved_bytes += scalar_saved_bytes;
  923|    218|  }
  924|    282|  return saved_bytes;
  925|    340|}
_ZNK7simdutf8westmere14implementation36convert_utf16le_to_utf32_with_errorsEPKDsmPDi:
  928|    442|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  929|       |  // ret.first.count is always the position in the buffer, not the number of
  930|       |  // code units written even if finished
  931|    442|  std::pair<result, char32_t *> ret =
  932|    442|      westmere::sse_convert_utf16_to_utf32_with_errors<endianness::LITTLE>(
  933|    442|          buf, len, utf32_output);
  934|    442|  if (ret.first.error) {
  ------------------
  |  Branch (934:7): [True: 158, False: 284]
  ------------------
  935|    158|    return ret.first;
  936|    158|  } // Can return directly since scalar fallback already found correct
  937|       |    // ret.first.count
  938|    284|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (938:7): [True: 230, False: 54]
  ------------------
  939|    230|    result scalar_res =
  940|    230|        scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
  941|    230|            buf + ret.first.count, len - ret.first.count, ret.second);
  942|    230|    if (scalar_res.error) {
  ------------------
  |  Branch (942:9): [True: 82, False: 148]
  ------------------
  943|     82|      scalar_res.count += ret.first.count;
  944|     82|      return scalar_res;
  945|    148|    } else {
  946|    148|      ret.second += scalar_res.count;
  947|    148|    }
  948|    230|  }
  949|    202|  ret.first.count =
  950|    202|      ret.second -
  951|    202|      utf32_output; // Set count to the number of 8-bit code units written
  952|    202|  return ret.first;
  953|    284|}
_ZNK7simdutf8westmere14implementation36convert_utf16be_to_utf32_with_errorsEPKDsmPDi:
  956|    360|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  957|       |  // ret.first.count is always the position in the buffer, not the number of
  958|       |  // code units written even if finished
  959|    360|  std::pair<result, char32_t *> ret =
  960|    360|      westmere::sse_convert_utf16_to_utf32_with_errors<endianness::BIG>(
  961|    360|          buf, len, utf32_output);
  962|    360|  if (ret.first.error) {
  ------------------
  |  Branch (962:7): [True: 124, False: 236]
  ------------------
  963|    124|    return ret.first;
  964|    124|  } // Can return directly since scalar fallback already found correct
  965|       |    // ret.first.count
  966|    236|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (966:7): [True: 196, False: 40]
  ------------------
  967|    196|    result scalar_res =
  968|    196|        scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
  969|    196|            buf + ret.first.count, len - ret.first.count, ret.second);
  970|    196|    if (scalar_res.error) {
  ------------------
  |  Branch (970:9): [True: 76, False: 120]
  ------------------
  971|     76|      scalar_res.count += ret.first.count;
  972|     76|      return scalar_res;
  973|    120|    } else {
  974|    120|      ret.second += scalar_res.count;
  975|    120|    }
  976|    196|  }
  977|    160|  ret.first.count =
  978|    160|      ret.second -
  979|    160|      utf32_output; // Set count to the number of 8-bit code units written
  980|    160|  return ret.first;
  981|    236|}
_ZNK7simdutf8westmere14implementation27convert_valid_utf32_to_utf8EPKDimPc:
  986|    216|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  987|    216|  return convert_utf32_to_utf8(buf, len, utf8_output);
  988|    216|}
_ZNK7simdutf8westmere14implementation24convert_utf32_to_utf16leEPKDimPDs:
  993|    806|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  994|    806|  std::pair<const char32_t *, char16_t *> ret =
  995|    806|      sse_convert_utf32_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
  996|    806|  if (ret.first == nullptr) {
  ------------------
  |  Branch (996:7): [True: 64, False: 742]
  ------------------
  997|     64|    return 0;
  998|     64|  }
  999|    742|  size_t saved_bytes = ret.second - utf16_output;
 1000|    742|  if (ret.first != buf + len) {
  ------------------
  |  Branch (1000:7): [True: 738, False: 4]
  ------------------
 1001|    738|    const size_t scalar_saved_bytes =
 1002|    738|        scalar::utf32_to_utf16::convert<endianness::LITTLE>(
 1003|    738|            ret.first, len - (ret.first - buf), ret.second);
 1004|    738|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (1004:9): [True: 302, False: 436]
  ------------------
 1005|    302|      return 0;
 1006|    302|    }
 1007|    436|    saved_bytes += scalar_saved_bytes;
 1008|    436|  }
 1009|    440|  return saved_bytes;
 1010|    742|}
_ZNK7simdutf8westmere14implementation24convert_utf32_to_utf16beEPKDimPDs:
 1013|    620|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1014|    620|  std::pair<const char32_t *, char16_t *> ret =
 1015|    620|      sse_convert_utf32_to_utf16<endianness::BIG>(buf, len, utf16_output);
 1016|    620|  if (ret.first == nullptr) {
  ------------------
  |  Branch (1016:7): [True: 72, False: 548]
  ------------------
 1017|     72|    return 0;
 1018|     72|  }
 1019|    548|  size_t saved_bytes = ret.second - utf16_output;
 1020|    548|  if (ret.first != buf + len) {
  ------------------
  |  Branch (1020:7): [True: 546, False: 2]
  ------------------
 1021|    546|    const size_t scalar_saved_bytes =
 1022|    546|        scalar::utf32_to_utf16::convert<endianness::BIG>(
 1023|    546|            ret.first, len - (ret.first - buf), ret.second);
 1024|    546|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (1024:9): [True: 270, False: 276]
  ------------------
 1025|    270|      return 0;
 1026|    270|    }
 1027|    276|    saved_bytes += scalar_saved_bytes;
 1028|    276|  }
 1029|    278|  return saved_bytes;
 1030|    548|}
_ZNK7simdutf8westmere14implementation36convert_utf32_to_utf16le_with_errorsEPKDimPDs:
 1033|    748|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1034|       |  // ret.first.count is always the position in the buffer, not the number of
 1035|       |  // code units written even if finished
 1036|    748|  std::pair<result, char16_t *> ret =
 1037|    748|      westmere::sse_convert_utf32_to_utf16_with_errors<endianness::LITTLE>(
 1038|    748|          buf, len, utf16_output);
 1039|    748|  if (ret.first.count != len) {
  ------------------
  |  Branch (1039:7): [True: 726, False: 22]
  ------------------
 1040|    726|    result scalar_res =
 1041|    726|        scalar::utf32_to_utf16::convert_with_errors<endianness::LITTLE>(
 1042|    726|            buf + ret.first.count, len - ret.first.count, ret.second);
 1043|    726|    if (scalar_res.error) {
  ------------------
  |  Branch (1043:9): [True: 568, False: 158]
  ------------------
 1044|    568|      scalar_res.count += ret.first.count;
 1045|    568|      return scalar_res;
 1046|    568|    } else {
 1047|    158|      ret.second += scalar_res.count;
 1048|    158|    }
 1049|    726|  }
 1050|    180|  ret.first.count =
 1051|    180|      ret.second -
 1052|    180|      utf16_output; // Set count to the number of 8-bit code units written
 1053|    180|  return ret.first;
 1054|    748|}
_ZNK7simdutf8westmere14implementation36convert_utf32_to_utf16be_with_errorsEPKDimPDs:
 1057|    670|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1058|       |  // ret.first.count is always the position in the buffer, not the number of
 1059|       |  // code units written even if finished
 1060|    670|  std::pair<result, char16_t *> ret =
 1061|    670|      westmere::sse_convert_utf32_to_utf16_with_errors<endianness::BIG>(
 1062|    670|          buf, len, utf16_output);
 1063|    670|  if (ret.first.count != len) {
  ------------------
  |  Branch (1063:7): [True: 646, False: 24]
  ------------------
 1064|    646|    result scalar_res =
 1065|    646|        scalar::utf32_to_utf16::convert_with_errors<endianness::BIG>(
 1066|    646|            buf + ret.first.count, len - ret.first.count, ret.second);
 1067|    646|    if (scalar_res.error) {
  ------------------
  |  Branch (1067:9): [True: 504, False: 142]
  ------------------
 1068|    504|      scalar_res.count += ret.first.count;
 1069|    504|      return scalar_res;
 1070|    504|    } else {
 1071|    142|      ret.second += scalar_res.count;
 1072|    142|    }
 1073|    646|  }
 1074|    166|  ret.first.count =
 1075|    166|      ret.second -
 1076|    166|      utf16_output; // Set count to the number of 8-bit code units written
 1077|    166|  return ret.first;
 1078|    670|}
_ZNK7simdutf8westmere14implementation30convert_valid_utf32_to_utf16leEPKDimPDs:
 1081|    320|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1082|    320|  return convert_utf32_to_utf16le(buf, len, utf16_output);
 1083|    320|}
_ZNK7simdutf8westmere14implementation30convert_valid_utf32_to_utf16beEPKDimPDs:
 1086|    132|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1087|    132|  return convert_utf32_to_utf16be(buf, len, utf16_output);
 1088|    132|}
_ZNK7simdutf8westmere14implementation30convert_valid_utf16le_to_utf32EPKDsmPDi:
 1091|    128|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
 1092|    128|  return convert_utf16le_to_utf32(buf, len, utf32_output);
 1093|    128|}
_ZNK7simdutf8westmere14implementation30convert_valid_utf16be_to_utf32EPKDsmPDi:
 1096|    180|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
 1097|    180|  return convert_utf16be_to_utf32(buf, len, utf32_output);
 1098|    180|}
_ZNK7simdutf8westmere14implementation13count_utf16leEPKDsm:
 1109|  1.43k|    const char16_t *input, size_t length) const noexcept {
 1110|  1.43k|  return utf16::count_code_points<endianness::LITTLE>(input, length);
 1111|  1.43k|}
_ZNK7simdutf8westmere14implementation13count_utf16beEPKDsm:
 1114|  1.32k|    const char16_t *input, size_t length) const noexcept {
 1115|  1.32k|  return utf16::count_code_points<endianness::BIG>(input, length);
 1116|  1.32k|}
_ZNK7simdutf8westmere14implementation10count_utf8EPKcm:
 1121|  4.92k|implementation::count_utf8(const char *input, size_t length) const noexcept {
 1122|  4.92k|  return utf8::count_code_points_bytemask(input, length);
 1123|  4.92k|}
_ZNK7simdutf8westmere14implementation23latin1_length_from_utf8EPKcm:
 1133|    720|    const char *buf, size_t len) const noexcept {
 1134|    720|  return count_utf8(buf, len);
 1135|    720|}
_ZNK7simdutf8westmere14implementation24utf8_length_from_utf16leEPKDsm:
 1140|    793|    const char16_t *input, size_t length) const noexcept {
 1141|    793|  return utf16::utf8_length_from_utf16_bytemask<endianness::LITTLE>(input,
 1142|    793|                                                                    length);
 1143|    793|}
_ZNK7simdutf8westmere14implementation24utf8_length_from_utf16beEPKDsm:
 1146|    723|    const char16_t *input, size_t length) const noexcept {
 1147|    723|  return utf16::utf8_length_from_utf16_bytemask<endianness::BIG>(input, length);
 1148|    723|}
_ZNK7simdutf8westmere14implementation23utf8_length_from_latin1EPKcm:
 1153|    280|    const char *input, size_t len) const noexcept {
 1154|    280|  const uint8_t *str = reinterpret_cast<const uint8_t *>(input);
 1155|    280|  size_t answer = len / sizeof(__m128i) * sizeof(__m128i);
 1156|    280|  size_t i = 0;
 1157|    280|  if (answer >= 2048) { // long strings optimization
  ------------------
  |  Branch (1157:7): [True: 133, False: 147]
  ------------------
 1158|    133|    __m128i two_64bits = _mm_setzero_si128();
 1159|  6.22k|    while (i + sizeof(__m128i) <= len) {
  ------------------
  |  Branch (1159:12): [True: 6.09k, False: 133]
  ------------------
 1160|  6.09k|      __m128i runner = _mm_setzero_si128();
 1161|  6.09k|      size_t iterations = (len - i) / sizeof(__m128i);
 1162|  6.09k|      if (iterations > 255) {
  ------------------
  |  Branch (1162:11): [True: 5.95k, False: 133]
  ------------------
 1163|  5.95k|        iterations = 255;
 1164|  5.95k|      }
 1165|  6.09k|      size_t max_i = i + iterations * sizeof(__m128i) - sizeof(__m128i);
 1166|   383k|      for (; i + 4 * sizeof(__m128i) <= max_i; i += 4 * sizeof(__m128i)) {
  ------------------
  |  Branch (1166:14): [True: 377k, False: 6.09k]
  ------------------
 1167|   377k|        __m128i input1 = _mm_loadu_si128((const __m128i *)(str + i));
 1168|   377k|        __m128i input2 =
 1169|   377k|            _mm_loadu_si128((const __m128i *)(str + i + sizeof(__m128i)));
 1170|   377k|        __m128i input3 =
 1171|   377k|            _mm_loadu_si128((const __m128i *)(str + i + 2 * sizeof(__m128i)));
 1172|   377k|        __m128i input4 =
 1173|   377k|            _mm_loadu_si128((const __m128i *)(str + i + 3 * sizeof(__m128i)));
 1174|   377k|        __m128i input12 =
 1175|   377k|            _mm_add_epi8(_mm_cmpgt_epi8(_mm_setzero_si128(), input1),
 1176|   377k|                         _mm_cmpgt_epi8(_mm_setzero_si128(), input2));
 1177|   377k|        __m128i input34 =
 1178|   377k|            _mm_add_epi8(_mm_cmpgt_epi8(_mm_setzero_si128(), input3),
 1179|   377k|                         _mm_cmpgt_epi8(_mm_setzero_si128(), input4));
 1180|   377k|        __m128i input1234 = _mm_add_epi8(input12, input34);
 1181|   377k|        runner = _mm_sub_epi8(runner, input1234);
 1182|   377k|      }
 1183|  24.2k|      for (; i <= max_i; i += sizeof(__m128i)) {
  ------------------
  |  Branch (1183:14): [True: 18.1k, False: 6.09k]
  ------------------
 1184|  18.1k|        __m128i more_input = _mm_loadu_si128((const __m128i *)(str + i));
 1185|  18.1k|        runner = _mm_sub_epi8(runner,
 1186|  18.1k|                              _mm_cmpgt_epi8(_mm_setzero_si128(), more_input));
 1187|  18.1k|      }
 1188|  6.09k|      two_64bits =
 1189|  6.09k|          _mm_add_epi64(two_64bits, _mm_sad_epu8(runner, _mm_setzero_si128()));
 1190|  6.09k|    }
 1191|    133|    answer +=
 1192|    133|        _mm_extract_epi64(two_64bits, 0) + _mm_extract_epi64(two_64bits, 1);
 1193|    147|  } else if (answer > 0) { // short string optimization
  ------------------
  |  Branch (1193:14): [True: 100, False: 47]
  ------------------
 1194|  1.02k|    for (; i + 2 * sizeof(__m128i) <= len; i += 2 * sizeof(__m128i)) {
  ------------------
  |  Branch (1194:12): [True: 922, False: 100]
  ------------------
 1195|    922|      __m128i latin = _mm_loadu_si128((const __m128i *)(input + i));
 1196|    922|      uint16_t non_ascii = (uint16_t)_mm_movemask_epi8(latin);
 1197|    922|      answer += count_ones(non_ascii);
 1198|    922|      latin = _mm_loadu_si128((const __m128i *)(input + i) + 1);
 1199|    922|      non_ascii = (uint16_t)_mm_movemask_epi8(latin);
 1200|    922|      answer += count_ones(non_ascii);
 1201|    922|    }
 1202|    151|    for (; i + sizeof(__m128i) <= len; i += sizeof(__m128i)) {
  ------------------
  |  Branch (1202:12): [True: 51, False: 100]
  ------------------
 1203|     51|      __m128i latin = _mm_loadu_si128((const __m128i *)(input + i));
 1204|     51|      uint16_t non_ascii = (uint16_t)_mm_movemask_epi8(latin);
 1205|     51|      answer += count_ones(non_ascii);
 1206|     51|    }
 1207|    100|  }
 1208|    280|  return answer + scalar::latin1::utf8_length_from_latin1(
 1209|    280|                      reinterpret_cast<const char *>(str + i), len - i);
 1210|    280|}
_ZNK7simdutf8westmere14implementation25utf32_length_from_utf16leEPKDsm:
 1215|    442|    const char16_t *input, size_t length) const noexcept {
 1216|    442|  return utf16::utf32_length_from_utf16<endianness::LITTLE>(input, length);
 1217|    442|}
_ZNK7simdutf8westmere14implementation25utf32_length_from_utf16beEPKDsm:
 1220|    426|    const char16_t *input, size_t length) const noexcept {
 1221|    426|  return utf16::utf32_length_from_utf16<endianness::BIG>(input, length);
 1222|    426|}
_ZNK7simdutf8westmere14implementation22utf16_length_from_utf8EPKcm:
 1227|  2.28k|    const char *input, size_t length) const noexcept {
 1228|  2.28k|  return utf8::utf16_length_from_utf8_bytemask(input, length);
 1229|  2.28k|}
_ZNK7simdutf8westmere14implementation22utf8_length_from_utf32EPKDim:
 1268|    913|    const char32_t *input, size_t length) const noexcept {
 1269|    913|  return utf32::utf8_length_from_utf32(input, length);
 1270|    913|}
_ZNK7simdutf8westmere14implementation23utf16_length_from_utf32EPKDim:
 1275|  1.43k|    const char32_t *input, size_t length) const noexcept {
 1276|  1.43k|  const __m128i v_00000000 = _mm_setzero_si128();
 1277|  1.43k|  const __m128i v_ffff0000 = _mm_set1_epi32((uint32_t)0xffff0000);
 1278|  1.43k|  size_t pos = 0;
 1279|  1.43k|  size_t count = 0;
 1280|   964k|  for (; pos + 4 <= length; pos += 4) {
  ------------------
  |  Branch (1280:10): [True: 962k, False: 1.43k]
  ------------------
 1281|   962k|    __m128i in = _mm_loadu_si128((__m128i *)(input + pos));
 1282|   962k|    const __m128i surrogate_bytemask =
 1283|   962k|        _mm_cmpeq_epi32(_mm_and_si128(in, v_ffff0000), v_00000000);
 1284|   962k|    const uint16_t surrogate_bitmask =
 1285|   962k|        static_cast<uint16_t>(_mm_movemask_epi8(surrogate_bytemask));
 1286|   962k|    size_t surrogate_count = (16 - count_ones(surrogate_bitmask)) / 4;
 1287|   962k|    count += 4 + surrogate_count;
 1288|   962k|  }
 1289|  1.43k|  return count +
 1290|  1.43k|         scalar::utf32::utf16_length_from_utf32(input + pos, length - pos);
 1291|  1.43k|}
_ZNK7simdutf8westmere14implementation22utf32_length_from_utf8EPKcm:
 1296|  1.20k|    const char *input, size_t length) const noexcept {
 1297|  1.20k|  return utf8::count_code_points(input, length);
 1298|  1.20k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_18is_asciiERKNS1_4simd8simd8x64IhEE:
   13|  5.54M|simdutf_really_inline bool is_ascii(const simd8x64<uint8_t> &input) {
   14|  5.54M|  return input.reduce_or().is_ascii();
   15|  5.54M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_124must_be_2_3_continuationENS1_4simd5simd8IhEES4_:
   22|  7.97M|                         const simd8<uint8_t> prev3) {
   23|  7.97M|  simd8<uint8_t> is_third_byte =
   24|  7.97M|      prev2.saturating_sub(0xe0u - 0x80); // Only 111_____ will be >= 0x80
   25|  7.97M|  simd8<uint8_t> is_fourth_byte =
   26|  7.97M|      prev3.saturating_sub(0xf0u - 0x80); // Only 1111____ will be >= 0x80
   27|  7.97M|  return simd8<bool>(is_third_byte | is_fourth_byte);
   28|  7.97M|}
_ZN7simdutf8westmere34convert_utf16_to_utf8_with_detailsILNS_10endiannessE0EEENS_11full_resultEPKDsmPc:
  750|    686|    const char16_t *buf, size_t len, char *utf8_output) {
  751|    686|  std::pair<result, char *> ret =
  752|    686|      westmere::sse_convert_utf16_to_utf8_with_errors<big_endian>(buf, len,
  753|    686|                                                                  utf8_output);
  754|    686|  if (ret.first.error) {
  ------------------
  |  Branch (754:7): [True: 94, False: 592]
  ------------------
  755|     94|    return full_result(ret.first.error, ret.first.count,
  756|     94|                       size_t(ret.second - utf8_output));
  757|     94|  }
  758|    592|  if (ret.first.count != len) {
  ------------------
  |  Branch (758:7): [True: 588, False: 4]
  ------------------
  759|    588|    full_result sres =
  760|    588|        scalar::utf16_to_utf8::convert_with_errors<big_endian, false>(
  761|    588|            buf + ret.first.count, len - ret.first.count, ret.second, 0);
  762|    588|    return full_result(sres.error, ret.first.count + sres.input_count,
  763|    588|                       size_t(ret.second - utf8_output) + sres.output_count);
  764|    588|  }
  765|      4|  return full_result(error_code::SUCCESS, len,
  766|      4|                     size_t(ret.second - utf8_output));
  767|    592|}
_ZN7simdutf8westmere34convert_utf16_to_utf8_with_detailsILNS_10endiannessE1EEENS_11full_resultEPKDsmPc:
  750|    556|    const char16_t *buf, size_t len, char *utf8_output) {
  751|    556|  std::pair<result, char *> ret =
  752|    556|      westmere::sse_convert_utf16_to_utf8_with_errors<big_endian>(buf, len,
  753|    556|                                                                  utf8_output);
  754|    556|  if (ret.first.error) {
  ------------------
  |  Branch (754:7): [True: 128, False: 428]
  ------------------
  755|    128|    return full_result(ret.first.error, ret.first.count,
  756|    128|                       size_t(ret.second - utf8_output));
  757|    128|  }
  758|    428|  if (ret.first.count != len) {
  ------------------
  |  Branch (758:7): [True: 424, False: 4]
  ------------------
  759|    424|    full_result sres =
  760|    424|        scalar::utf16_to_utf8::convert_with_errors<big_endian, false>(
  761|    424|            buf + ret.first.count, len - ret.first.count, ret.second, 0);
  762|    424|    return full_result(sres.error, ret.first.count + sres.input_count,
  763|    424|                       size_t(ret.second - utf8_output) + sres.output_count);
  764|    424|  }
  765|      4|  return full_result(error_code::SUCCESS, len,
  766|      4|                     size_t(ret.second - utf8_output));
  767|    428|}

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

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_127sse_convert_latin1_to_utf16ILNS_10endiannessE0EEENSt3__14pairIPKcPDsEES7_mS8_:
    4|     76|                            char16_t *utf16_output) {
    5|     76|  size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16
    6|   129k|  for (size_t i = 0; i < rounded_len; i += 16) {
  ------------------
  |  Branch (6:22): [True: 129k, False: 76]
  ------------------
    7|       |    // Load 16 Latin1 characters into a 128-bit register
    8|   129k|    __m128i in =
    9|   129k|        _mm_loadu_si128(reinterpret_cast<const __m128i *>(&latin1_input[i]));
   10|   129k|    __m128i out1 = big_endian ? _mm_unpacklo_epi8(_mm_setzero_si128(), in)
  ------------------
  |  Branch (10:20): [Folded, False: 129k]
  ------------------
   11|   129k|                              : _mm_unpacklo_epi8(in, _mm_setzero_si128());
   12|   129k|    __m128i out2 = big_endian ? _mm_unpackhi_epi8(_mm_setzero_si128(), in)
  ------------------
  |  Branch (12:20): [Folded, False: 129k]
  ------------------
   13|   129k|                              : _mm_unpackhi_epi8(in, _mm_setzero_si128());
   14|       |    // Zero extend each Latin1 character to 16-bit integers and store the
   15|       |    // results back to memory
   16|   129k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(&utf16_output[i]), out1);
   17|   129k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(&utf16_output[i + 8]), out2);
   18|   129k|  }
   19|       |  // return pointers pointing to where we left off
   20|     76|  return std::make_pair(latin1_input + rounded_len, utf16_output + rounded_len);
   21|     76|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_127sse_convert_latin1_to_utf16ILNS_10endiannessE1EEENSt3__14pairIPKcPDsEES7_mS8_:
    4|    110|                            char16_t *utf16_output) {
    5|    110|  size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16
    6|   143k|  for (size_t i = 0; i < rounded_len; i += 16) {
  ------------------
  |  Branch (6:22): [True: 143k, False: 110]
  ------------------
    7|       |    // Load 16 Latin1 characters into a 128-bit register
    8|   143k|    __m128i in =
    9|   143k|        _mm_loadu_si128(reinterpret_cast<const __m128i *>(&latin1_input[i]));
   10|   143k|    __m128i out1 = big_endian ? _mm_unpacklo_epi8(_mm_setzero_si128(), in)
  ------------------
  |  Branch (10:20): [True: 143k, Folded]
  ------------------
   11|   143k|                              : _mm_unpacklo_epi8(in, _mm_setzero_si128());
   12|   143k|    __m128i out2 = big_endian ? _mm_unpackhi_epi8(_mm_setzero_si128(), in)
  ------------------
  |  Branch (12:20): [True: 143k, Folded]
  ------------------
   13|   143k|                              : _mm_unpackhi_epi8(in, _mm_setzero_si128());
   14|       |    // Zero extend each Latin1 character to 16-bit integers and store the
   15|       |    // results back to memory
   16|   143k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(&utf16_output[i]), out1);
   17|   143k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(&utf16_output[i + 8]), out2);
   18|   143k|  }
   19|       |  // return pointers pointing to where we left off
   20|    110|  return std::make_pair(latin1_input + rounded_len, utf16_output + rounded_len);
   21|    110|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_127sse_convert_latin1_to_utf32EPKcmPDi:
    3|     96|                            char32_t *utf32_output) {
    4|     96|  const char *end = buf + len;
    5|       |
    6|   319k|  while (end - buf >= 16) {
  ------------------
  |  Branch (6:10): [True: 318k, False: 96]
  ------------------
    7|       |    // Load 16 Latin1 characters (16 bytes) into a 128-bit register
    8|   318k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
    9|       |
   10|       |    // Shift input to process next 4 bytes
   11|   318k|    __m128i in_shifted1 = _mm_srli_si128(in, 4);
   12|   318k|    __m128i in_shifted2 = _mm_srli_si128(in, 8);
   13|   318k|    __m128i in_shifted3 = _mm_srli_si128(in, 12);
   14|       |
   15|       |    // expand 8-bit to 32-bit unit
   16|   318k|    __m128i out1 = _mm_cvtepu8_epi32(in);
   17|   318k|    __m128i out2 = _mm_cvtepu8_epi32(in_shifted1);
   18|   318k|    __m128i out3 = _mm_cvtepu8_epi32(in_shifted2);
   19|   318k|    __m128i out4 = _mm_cvtepu8_epi32(in_shifted3);
   20|       |
   21|   318k|    _mm_storeu_si128((__m128i *)utf32_output, out1);
   22|   318k|    _mm_storeu_si128((__m128i *)(utf32_output + 4), out2);
   23|   318k|    _mm_storeu_si128((__m128i *)(utf32_output + 8), out3);
   24|   318k|    _mm_storeu_si128((__m128i *)(utf32_output + 12), out4);
   25|       |
   26|   318k|    utf32_output += 16;
   27|   318k|    buf += 16;
   28|   318k|  }
   29|       |
   30|     96|  return std::make_pair(buf, utf32_output);
   31|     96|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_126sse_convert_latin1_to_utf8EPKcmPc:
    3|    560|                           const size_t latin_input_length, char *utf8_output) {
    4|    560|  const char *end = latin_input + latin_input_length;
    5|       |
    6|    560|  const __m128i v_0000 = _mm_setzero_si128();
    7|       |  // 0b1000_0000
    8|    560|  const __m128i v_80 = _mm_set1_epi8((uint8_t)0x80);
    9|       |  // 0b1111_1111_1000_0000
   10|    560|  const __m128i v_ff80 = _mm_set1_epi16((uint16_t)0xff80);
   11|       |
   12|    560|  const __m128i latin_1_half_into_u16_byte_mask =
   13|    560|      _mm_setr_epi8(0, '\x80', 1, '\x80', 2, '\x80', 3, '\x80', 4, '\x80', 5,
   14|    560|                    '\x80', 6, '\x80', 7, '\x80');
   15|       |
   16|    560|  const __m128i latin_2_half_into_u16_byte_mask =
   17|    560|      _mm_setr_epi8(8, '\x80', 9, '\x80', 10, '\x80', 11, '\x80', 12, '\x80',
   18|    560|                    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|  3.06M|  while (end - latin_input >= 16 + 8) {
  ------------------
  |  Branch (25:10): [True: 3.06M, False: 560]
  ------------------
   26|       |    // Load 16 Latin1 characters (16 bytes) into a 128-bit register
   27|  3.06M|    __m128i v_latin = _mm_loadu_si128((__m128i *)latin_input);
   28|       |
   29|  3.06M|    if (_mm_testz_si128(v_latin, v_80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (29:9): [True: 2.76M, False: 299k]
  ------------------
   30|  2.76M|      _mm_storeu_si128((__m128i *)utf8_output, v_latin);
   31|  2.76M|      latin_input += 16;
   32|  2.76M|      utf8_output += 16;
   33|  2.76M|      continue;
   34|  2.76M|    }
   35|       |
   36|       |    // assuming a/b are bytes and A/B are uint16 of the same value
   37|       |    // aaaa_aaaa_bbbb_bbbb -> AAAA_AAAA
   38|   299k|    __m128i v_u16_latin_1_half =
   39|   299k|        _mm_shuffle_epi8(v_latin, latin_1_half_into_u16_byte_mask);
   40|       |    // aaaa_aaaa_bbbb_bbbb -> BBBB_BBBB
   41|   299k|    __m128i v_u16_latin_2_half =
   42|   299k|        _mm_shuffle_epi8(v_latin, latin_2_half_into_u16_byte_mask);
   43|       |
   44|   299k|    internal::westmere::write_v_u16_11bits_to_utf8(v_u16_latin_1_half,
   45|   299k|                                                   utf8_output, v_0000, v_ff80);
   46|   299k|    internal::westmere::write_v_u16_11bits_to_utf8(v_u16_latin_2_half,
   47|   299k|                                                   utf8_output, v_0000, v_ff80);
   48|   299k|    latin_input += 16;
   49|   299k|  }
   50|       |
   51|    560|  if (end - latin_input >= 16) {
  ------------------
  |  Branch (51:7): [True: 294, False: 266]
  ------------------
   52|       |    // Load 16 Latin1 characters (16 bytes) into a 128-bit register
   53|    294|    __m128i v_latin = _mm_loadu_si128((__m128i *)latin_input);
   54|       |
   55|    294|    if (_mm_testz_si128(v_latin, v_80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (55:9): [True: 134, False: 160]
  ------------------
   56|    134|      _mm_storeu_si128((__m128i *)utf8_output, v_latin);
   57|    134|      latin_input += 16;
   58|    134|      utf8_output += 16;
   59|    160|    } else {
   60|       |      // assuming a/b are bytes and A/B are uint16 of the same value
   61|       |      // aaaa_aaaa_bbbb_bbbb -> AAAA_AAAA
   62|    160|      __m128i v_u16_latin_1_half =
   63|    160|          _mm_shuffle_epi8(v_latin, latin_1_half_into_u16_byte_mask);
   64|    160|      internal::westmere::write_v_u16_11bits_to_utf8(
   65|    160|          v_u16_latin_1_half, utf8_output, v_0000, v_ff80);
   66|    160|      latin_input += 8;
   67|    160|    }
   68|    294|  }
   69|       |
   70|    560|  return std::make_pair(latin_input, utf8_output);
   71|    560|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_127sse_convert_utf16_to_latin1ILNS_10endiannessE0EEENSt3__14pairIPKDsPcEES7_mS8_:
    4|    120|                            char *latin1_output) {
    5|    120|  const char16_t *end = buf + len;
    6|   152k|  while (end - buf >= 8) {
  ------------------
  |  Branch (6:10): [True: 152k, False: 82]
  ------------------
    7|       |    // Load 8 UTF-16 characters into 128-bit SSE register
    8|   152k|    __m128i in = _mm_loadu_si128(reinterpret_cast<const __m128i *>(buf));
    9|       |
   10|       |    if constexpr (!match_system(big_endian)) {
   11|       |      const __m128i swap =
   12|       |          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   13|       |      in = _mm_shuffle_epi8(in, swap);
   14|       |    }
   15|       |
   16|   152k|    __m128i high_byte_mask = _mm_set1_epi16((int16_t)0xFF00);
   17|   152k|    if (_mm_testz_si128(in, high_byte_mask)) {
  ------------------
  |  Branch (17:9): [True: 152k, False: 38]
  ------------------
   18|       |      // Pack 16-bit characters into 8-bit and store in latin1_output
   19|   152k|      __m128i latin1_packed = _mm_packus_epi16(in, in);
   20|   152k|      _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output),
   21|   152k|                       latin1_packed);
   22|       |      // Adjust pointers for next iteration
   23|   152k|      buf += 8;
   24|   152k|      latin1_output += 8;
   25|   152k|    } else {
   26|     38|      return std::make_pair(nullptr, reinterpret_cast<char *>(latin1_output));
   27|     38|    }
   28|   152k|  } // while
   29|     82|  return std::make_pair(buf, latin1_output);
   30|    120|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_127sse_convert_utf16_to_latin1ILNS_10endiannessE1EEENSt3__14pairIPKDsPcEES7_mS8_:
    4|     98|                            char *latin1_output) {
    5|     98|  const char16_t *end = buf + len;
    6|    878|  while (end - buf >= 8) {
  ------------------
  |  Branch (6:10): [True: 798, False: 80]
  ------------------
    7|       |    // Load 8 UTF-16 characters into 128-bit SSE register
    8|    798|    __m128i in = _mm_loadu_si128(reinterpret_cast<const __m128i *>(buf));
    9|       |
   10|    798|    if constexpr (!match_system(big_endian)) {
   11|    798|      const __m128i swap =
   12|    798|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   13|    798|      in = _mm_shuffle_epi8(in, swap);
   14|    798|    }
   15|       |
   16|    798|    __m128i high_byte_mask = _mm_set1_epi16((int16_t)0xFF00);
   17|    798|    if (_mm_testz_si128(in, high_byte_mask)) {
  ------------------
  |  Branch (17:9): [True: 780, False: 18]
  ------------------
   18|       |      // Pack 16-bit characters into 8-bit and store in latin1_output
   19|    780|      __m128i latin1_packed = _mm_packus_epi16(in, in);
   20|    780|      _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output),
   21|    780|                       latin1_packed);
   22|       |      // Adjust pointers for next iteration
   23|    780|      buf += 8;
   24|    780|      latin1_output += 8;
   25|    780|    } else {
   26|     18|      return std::make_pair(nullptr, reinterpret_cast<char *>(latin1_output));
   27|     18|    }
   28|    798|  } // while
   29|     80|  return std::make_pair(buf, latin1_output);
   30|     98|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_139sse_convert_utf16_to_latin1_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
   35|    280|                                        char *latin1_output) {
   36|    280|  const char16_t *start = buf;
   37|    280|  const char16_t *end = buf + len;
   38|   837k|  while (end - buf >= 8) {
  ------------------
  |  Branch (38:10): [True: 837k, False: 140]
  ------------------
   39|   837k|    __m128i in = _mm_loadu_si128(reinterpret_cast<const __m128i *>(buf));
   40|       |
   41|       |    if constexpr (!match_system(big_endian)) {
   42|       |      const __m128i swap =
   43|       |          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   44|       |      in = _mm_shuffle_epi8(in, swap);
   45|       |    }
   46|       |
   47|   837k|    __m128i high_byte_mask = _mm_set1_epi16((int16_t)0xFF00);
   48|   837k|    if (_mm_testz_si128(in, high_byte_mask)) {
  ------------------
  |  Branch (48:9): [True: 837k, False: 140]
  ------------------
   49|   837k|      __m128i latin1_packed = _mm_packus_epi16(in, in);
   50|   837k|      _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output),
   51|   837k|                       latin1_packed);
   52|   837k|      buf += 8;
   53|   837k|      latin1_output += 8;
   54|   837k|    } else {
   55|       |      // Fallback to scalar code for handling errors
   56|    452|      for (int k = 0; k < 8; k++) {
  ------------------
  |  Branch (56:23): [True: 452, False: 0]
  ------------------
   57|    452|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
   58|    452|        if (word <= 0xff) {
  ------------------
  |  Branch (58:13): [True: 312, False: 140]
  ------------------
   59|    312|          *latin1_output++ = char(word);
   60|    312|        } else {
   61|    140|          return std::make_pair(result(error_code::TOO_LARGE, buf - start + k),
   62|    140|                                latin1_output);
   63|    140|        }
   64|    452|      }
   65|      0|      buf += 8;
   66|      0|    }
   67|   837k|  } // while
   68|    140|  return std::make_pair(result(error_code::SUCCESS, buf - start),
   69|    140|                        latin1_output);
   70|    280|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_139sse_convert_utf16_to_latin1_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
   35|    248|                                        char *latin1_output) {
   36|    248|  const char16_t *start = buf;
   37|    248|  const char16_t *end = buf + len;
   38|   914k|  while (end - buf >= 8) {
  ------------------
  |  Branch (38:10): [True: 914k, False: 120]
  ------------------
   39|   914k|    __m128i in = _mm_loadu_si128(reinterpret_cast<const __m128i *>(buf));
   40|       |
   41|   914k|    if constexpr (!match_system(big_endian)) {
   42|   914k|      const __m128i swap =
   43|   914k|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   44|   914k|      in = _mm_shuffle_epi8(in, swap);
   45|   914k|    }
   46|       |
   47|   914k|    __m128i high_byte_mask = _mm_set1_epi16((int16_t)0xFF00);
   48|   914k|    if (_mm_testz_si128(in, high_byte_mask)) {
  ------------------
  |  Branch (48:9): [True: 914k, False: 128]
  ------------------
   49|   914k|      __m128i latin1_packed = _mm_packus_epi16(in, in);
   50|   914k|      _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output),
   51|   914k|                       latin1_packed);
   52|   914k|      buf += 8;
   53|   914k|      latin1_output += 8;
   54|   914k|    } else {
   55|       |      // Fallback to scalar code for handling errors
   56|    434|      for (int k = 0; k < 8; k++) {
  ------------------
  |  Branch (56:23): [True: 434, False: 0]
  ------------------
   57|    434|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
   58|    434|        if (word <= 0xff) {
  ------------------
  |  Branch (58:13): [True: 306, False: 128]
  ------------------
   59|    306|          *latin1_output++ = char(word);
   60|    306|        } else {
   61|    128|          return std::make_pair(result(error_code::TOO_LARGE, buf - start + k),
   62|    128|                                latin1_output);
   63|    128|        }
   64|    434|      }
   65|      0|      buf += 8;
   66|      0|    }
   67|   914k|  } // while
   68|    120|  return std::make_pair(result(error_code::SUCCESS, buf - start),
   69|    120|                        latin1_output);
   70|    248|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_126sse_convert_utf16_to_utf32ILNS_10endiannessE0EEENSt3__14pairIPKDsPDiEES7_mS8_:
   57|    430|                           char32_t *utf32_output) {
   58|    430|  const char16_t *end = buf + len;
   59|       |
   60|    430|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
   61|    430|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
   62|       |
   63|   392k|  while (end - buf >= 8) {
  ------------------
  |  Branch (63:10): [True: 392k, False: 314]
  ------------------
   64|   392k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
   65|       |
   66|   392k|    if (big_endian) {
  ------------------
  |  Branch (66:9): [Folded, False: 392k]
  ------------------
   67|      0|      const __m128i swap =
   68|      0|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   69|      0|      in = _mm_shuffle_epi8(in, swap);
   70|      0|    }
   71|       |
   72|       |    // 1. Check if there are any surrogate word in the input chunk.
   73|       |    //    We have also deal with situation when there is a surrogate word
   74|       |    //    at the end of a chunk.
   75|   392k|    const __m128i surrogates_bytemask =
   76|   392k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800);
   77|       |
   78|       |    // bitmask = 0x0000 if there are no surrogates
   79|       |    //         = 0xc000 if the last word is a surrogate
   80|   392k|    const uint16_t surrogates_bitmask =
   81|   392k|        static_cast<uint16_t>(_mm_movemask_epi8(surrogates_bytemask));
   82|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
   83|       |    // However, it is likely an uncommon occurrence.
   84|   392k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (84:9): [True: 386k, False: 6.25k]
  ------------------
   85|       |      // case: no surrogate pair, extend 16-bit code units to 32-bit code units
   86|   386k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output),
   87|   386k|                       _mm_cvtepu16_epi32(in));
   88|   386k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 4),
   89|   386k|                       _mm_cvtepu16_epi32(_mm_srli_si128(in, 8)));
   90|   386k|      utf32_output += 8;
   91|   386k|      buf += 8;
   92|       |      // surrogate pair(s) in a register
   93|   386k|    } else {
   94|       |      // Let us do a scalar fallback.
   95|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
   96|       |      // in the presence of surrogate pairs may require non-trivial tables.
   97|  6.25k|      size_t forward = 15;
   98|  6.25k|      size_t k = 0;
   99|  6.25k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (99:11): [True: 86, False: 6.16k]
  ------------------
  100|     86|        forward = size_t(end - buf - 1);
  101|     86|      }
  102|  90.7k|      for (; k < forward; k++) {
  ------------------
  |  Branch (102:14): [True: 84.6k, False: 6.13k]
  ------------------
  103|  84.6k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  104|  84.6k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (104:13): [True: 76.8k, False: 7.81k]
  ------------------
  105|  76.8k|          *utf32_output++ = char32_t(word);
  106|  76.8k|        } else {
  107|       |          // must be a surrogate pair
  108|  7.81k|          uint16_t diff = uint16_t(word - 0xD800);
  109|  7.81k|          uint16_t next_word =
  110|  7.81k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  111|  7.81k|          k++;
  112|  7.81k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  113|  7.81k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (113:15): [True: 116, False: 7.70k]
  ------------------
  114|    116|            return std::make_pair(nullptr, utf32_output);
  115|    116|          }
  116|  7.70k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  117|  7.70k|          *utf32_output++ = char32_t(value);
  118|  7.70k|        }
  119|  84.6k|      }
  120|  6.13k|      buf += k;
  121|  6.13k|    }
  122|   392k|  } // while
  123|    314|  return std::make_pair(buf, utf32_output);
  124|    430|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_126sse_convert_utf16_to_utf32ILNS_10endiannessE1EEENSt3__14pairIPKDsPDiEES7_mS8_:
   57|    458|                           char32_t *utf32_output) {
   58|    458|  const char16_t *end = buf + len;
   59|       |
   60|    458|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
   61|    458|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
   62|       |
   63|   943k|  while (end - buf >= 8) {
  ------------------
  |  Branch (63:10): [True: 942k, False: 346]
  ------------------
   64|   942k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
   65|       |
   66|   942k|    if (big_endian) {
  ------------------
  |  Branch (66:9): [True: 942k, Folded]
  ------------------
   67|   942k|      const __m128i swap =
   68|   942k|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   69|   942k|      in = _mm_shuffle_epi8(in, swap);
   70|   942k|    }
   71|       |
   72|       |    // 1. Check if there are any surrogate word in the input chunk.
   73|       |    //    We have also deal with situation when there is a surrogate word
   74|       |    //    at the end of a chunk.
   75|   942k|    const __m128i surrogates_bytemask =
   76|   942k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800);
   77|       |
   78|       |    // bitmask = 0x0000 if there are no surrogates
   79|       |    //         = 0xc000 if the last word is a surrogate
   80|   942k|    const uint16_t surrogates_bitmask =
   81|   942k|        static_cast<uint16_t>(_mm_movemask_epi8(surrogates_bytemask));
   82|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
   83|       |    // However, it is likely an uncommon occurrence.
   84|   942k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (84:9): [True: 931k, False: 11.4k]
  ------------------
   85|       |      // case: no surrogate pair, extend 16-bit code units to 32-bit code units
   86|   931k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output),
   87|   931k|                       _mm_cvtepu16_epi32(in));
   88|   931k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 4),
   89|   931k|                       _mm_cvtepu16_epi32(_mm_srli_si128(in, 8)));
   90|   931k|      utf32_output += 8;
   91|   931k|      buf += 8;
   92|       |      // surrogate pair(s) in a register
   93|   931k|    } else {
   94|       |      // Let us do a scalar fallback.
   95|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
   96|       |      // in the presence of surrogate pairs may require non-trivial tables.
   97|  11.4k|      size_t forward = 15;
   98|  11.4k|      size_t k = 0;
   99|  11.4k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (99:11): [True: 92, False: 11.3k]
  ------------------
  100|     92|        forward = size_t(end - buf - 1);
  101|     92|      }
  102|   170k|      for (; k < forward; k++) {
  ------------------
  |  Branch (102:14): [True: 158k, False: 11.3k]
  ------------------
  103|   158k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  104|   158k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (104:13): [True: 146k, False: 12.2k]
  ------------------
  105|   146k|          *utf32_output++ = char32_t(word);
  106|   146k|        } else {
  107|       |          // must be a surrogate pair
  108|  12.2k|          uint16_t diff = uint16_t(word - 0xD800);
  109|  12.2k|          uint16_t next_word =
  110|  12.2k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  111|  12.2k|          k++;
  112|  12.2k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  113|  12.2k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (113:15): [True: 112, False: 12.1k]
  ------------------
  114|    112|            return std::make_pair(nullptr, utf32_output);
  115|    112|          }
  116|  12.1k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  117|  12.1k|          *utf32_output++ = char32_t(value);
  118|  12.1k|        }
  119|   158k|      }
  120|  11.3k|      buf += k;
  121|  11.3k|    }
  122|   942k|  } // while
  123|    346|  return std::make_pair(buf, utf32_output);
  124|    458|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_138sse_convert_utf16_to_utf32_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPDiEEPKDsmS7_:
  136|    442|                                       char32_t *utf32_output) {
  137|    442|  const char16_t *start = buf;
  138|    442|  const char16_t *end = buf + len;
  139|       |
  140|    442|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
  141|    442|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
  142|       |
  143|   398k|  while (end - buf >= 8) {
  ------------------
  |  Branch (143:10): [True: 398k, False: 284]
  ------------------
  144|   398k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
  145|       |
  146|   398k|    if (big_endian) {
  ------------------
  |  Branch (146:9): [Folded, False: 398k]
  ------------------
  147|      0|      const __m128i swap =
  148|      0|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  149|      0|      in = _mm_shuffle_epi8(in, swap);
  150|      0|    }
  151|       |
  152|       |    // 1. Check if there are any surrogate word in the input chunk.
  153|       |    //    We have also deal with situation when there is a surrogate word
  154|       |    //    at the end of a chunk.
  155|   398k|    const __m128i surrogates_bytemask =
  156|   398k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800);
  157|       |
  158|       |    // bitmask = 0x0000 if there are no surrogates
  159|       |    //         = 0xc000 if the last word is a surrogate
  160|   398k|    const uint16_t surrogates_bitmask =
  161|   398k|        static_cast<uint16_t>(_mm_movemask_epi8(surrogates_bytemask));
  162|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
  163|       |    // However, it is likely an uncommon occurrence.
  164|   398k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (164:9): [True: 392k, False: 6.26k]
  ------------------
  165|       |      // case: no surrogate pair, extend 16-bit code units to 32-bit code units
  166|   392k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output),
  167|   392k|                       _mm_cvtepu16_epi32(in));
  168|   392k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 4),
  169|   392k|                       _mm_cvtepu16_epi32(_mm_srli_si128(in, 8)));
  170|   392k|      utf32_output += 8;
  171|   392k|      buf += 8;
  172|       |      // surrogate pair(s) in a register
  173|   392k|    } else {
  174|       |      // Let us do a scalar fallback.
  175|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
  176|       |      // in the presence of surrogate pairs may require non-trivial tables.
  177|  6.26k|      size_t forward = 15;
  178|  6.26k|      size_t k = 0;
  179|  6.26k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (179:11): [True: 94, False: 6.16k]
  ------------------
  180|     94|        forward = size_t(end - buf - 1);
  181|     94|      }
  182|  87.8k|      for (; k < forward; k++) {
  ------------------
  |  Branch (182:14): [True: 81.7k, False: 6.10k]
  ------------------
  183|  81.7k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  184|  81.7k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (184:13): [True: 70.7k, False: 10.9k]
  ------------------
  185|  70.7k|          *utf32_output++ = char32_t(word);
  186|  70.7k|        } else {
  187|       |          // must be a surrogate pair
  188|  10.9k|          uint16_t diff = uint16_t(word - 0xD800);
  189|  10.9k|          uint16_t next_word =
  190|  10.9k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  191|  10.9k|          k++;
  192|  10.9k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  193|  10.9k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (193:15): [True: 158, False: 10.8k]
  ------------------
  194|    158|            return std::make_pair(
  195|    158|                result(error_code::SURROGATE, buf - start + k - 1),
  196|    158|                utf32_output);
  197|    158|          }
  198|  10.8k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  199|  10.8k|          *utf32_output++ = char32_t(value);
  200|  10.8k|        }
  201|  81.7k|      }
  202|  6.10k|      buf += k;
  203|  6.10k|    }
  204|   398k|  } // while
  205|    284|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf32_output);
  206|    442|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_138sse_convert_utf16_to_utf32_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPDiEEPKDsmS7_:
  136|    360|                                       char32_t *utf32_output) {
  137|    360|  const char16_t *start = buf;
  138|    360|  const char16_t *end = buf + len;
  139|       |
  140|    360|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
  141|    360|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
  142|       |
  143|   212k|  while (end - buf >= 8) {
  ------------------
  |  Branch (143:10): [True: 212k, False: 236]
  ------------------
  144|   212k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
  145|       |
  146|   212k|    if (big_endian) {
  ------------------
  |  Branch (146:9): [True: 212k, Folded]
  ------------------
  147|   212k|      const __m128i swap =
  148|   212k|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  149|   212k|      in = _mm_shuffle_epi8(in, swap);
  150|   212k|    }
  151|       |
  152|       |    // 1. Check if there are any surrogate word in the input chunk.
  153|       |    //    We have also deal with situation when there is a surrogate word
  154|       |    //    at the end of a chunk.
  155|   212k|    const __m128i surrogates_bytemask =
  156|   212k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800);
  157|       |
  158|       |    // bitmask = 0x0000 if there are no surrogates
  159|       |    //         = 0xc000 if the last word is a surrogate
  160|   212k|    const uint16_t surrogates_bitmask =
  161|   212k|        static_cast<uint16_t>(_mm_movemask_epi8(surrogates_bytemask));
  162|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
  163|       |    // However, it is likely an uncommon occurrence.
  164|   212k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (164:9): [True: 206k, False: 5.98k]
  ------------------
  165|       |      // case: no surrogate pair, extend 16-bit code units to 32-bit code units
  166|   206k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output),
  167|   206k|                       _mm_cvtepu16_epi32(in));
  168|   206k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 4),
  169|   206k|                       _mm_cvtepu16_epi32(_mm_srli_si128(in, 8)));
  170|   206k|      utf32_output += 8;
  171|   206k|      buf += 8;
  172|       |      // surrogate pair(s) in a register
  173|   206k|    } else {
  174|       |      // Let us do a scalar fallback.
  175|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
  176|       |      // in the presence of surrogate pairs may require non-trivial tables.
  177|  5.98k|      size_t forward = 15;
  178|  5.98k|      size_t k = 0;
  179|  5.98k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (179:11): [True: 94, False: 5.89k]
  ------------------
  180|     94|        forward = size_t(end - buf - 1);
  181|     94|      }
  182|  84.0k|      for (; k < forward; k++) {
  ------------------
  |  Branch (182:14): [True: 78.2k, False: 5.86k]
  ------------------
  183|  78.2k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  184|  78.2k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (184:13): [True: 67.4k, False: 10.7k]
  ------------------
  185|  67.4k|          *utf32_output++ = char32_t(word);
  186|  67.4k|        } else {
  187|       |          // must be a surrogate pair
  188|  10.7k|          uint16_t diff = uint16_t(word - 0xD800);
  189|  10.7k|          uint16_t next_word =
  190|  10.7k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  191|  10.7k|          k++;
  192|  10.7k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  193|  10.7k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (193:15): [True: 124, False: 10.6k]
  ------------------
  194|    124|            return std::make_pair(
  195|    124|                result(error_code::SURROGATE, buf - start + k - 1),
  196|    124|                utf32_output);
  197|    124|          }
  198|  10.6k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  199|  10.6k|          *utf32_output++ = char32_t(value);
  200|  10.6k|        }
  201|  78.2k|      }
  202|  5.86k|      buf += k;
  203|  5.86k|    }
  204|   212k|  } // while
  205|    236|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf32_output);
  206|    360|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_125sse_convert_utf16_to_utf8ILNS_10endiannessE0EEENSt3__14pairIPKDsPcEES7_mS8_:
   56|    874|sse_convert_utf16_to_utf8(const char16_t *buf, size_t len, char *utf8_output) {
   57|       |
   58|    874|  const char16_t *end = buf + len;
   59|       |
   60|    874|  const __m128i v_0000 = _mm_setzero_si128();
   61|    874|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
   62|    874|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
   63|    874|  const size_t safety_margin =
   64|    874|      12; // to avoid overruns, see issue
   65|       |          // https://github.com/simdutf/simdutf/issues/92
   66|       |
   67|  1.17M|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (67:10): [True: 1.17M, False: 756]
  ------------------
   68|  1.17M|    __m128i in = _mm_loadu_si128((__m128i *)buf);
   69|  1.17M|    if (big_endian) {
  ------------------
  |  Branch (69:9): [Folded, False: 1.17M]
  ------------------
   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|  1.17M|    const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80);
   76|  1.17M|    if (_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (76:9): [True: 869k, False: 307k]
  ------------------
   77|   869k|      __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
   78|   869k|      if (big_endian) {
  ------------------
  |  Branch (78:11): [Folded, False: 869k]
  ------------------
   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|   869k|      if (!_mm_testz_si128(nextin, v_ff80)) {
  ------------------
  |  Branch (83:11): [True: 38.2k, False: 831k]
  ------------------
   84|       |        // 1. pack the bytes
   85|       |        // obviously suboptimal.
   86|  38.2k|        const __m128i utf8_packed = _mm_packus_epi16(in, in);
   87|       |        // 2. store (16 bytes)
   88|  38.2k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   89|       |        // 3. adjust pointers
   90|  38.2k|        buf += 8;
   91|  38.2k|        utf8_output += 8;
   92|  38.2k|        in = nextin;
   93|   831k|      } else {
   94|       |        // 1. pack the bytes
   95|       |        // obviously suboptimal.
   96|   831k|        const __m128i utf8_packed = _mm_packus_epi16(in, nextin);
   97|       |        // 2. store (16 bytes)
   98|   831k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   99|       |        // 3. adjust pointers
  100|   831k|        buf += 16;
  101|   831k|        utf8_output += 16;
  102|   831k|        continue; // we are done for this round!
  103|   831k|      }
  104|   869k|    }
  105|       |
  106|       |    // no bits set above 7th bit
  107|   346k|    const __m128i one_byte_bytemask =
  108|   346k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000);
  109|   346k|    const uint16_t one_byte_bitmask =
  110|   346k|        static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
  111|       |
  112|       |    // no bits set above 11th bit
  113|   346k|    const __m128i one_or_two_bytes_bytemask =
  114|   346k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000);
  115|   346k|    const uint16_t one_or_two_bytes_bitmask =
  116|   346k|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  117|       |
  118|   346k|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (118:9): [True: 64.3k, False: 281k]
  ------------------
  119|  64.3k|      internal::westmere::write_v_u16_11bits_to_utf8(
  120|  64.3k|          in, utf8_output, one_byte_bytemask, one_byte_bitmask);
  121|  64.3k|      buf += 8;
  122|  64.3k|      continue;
  123|  64.3k|    }
  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|   281k|    const __m128i surrogates_bytemask =
  129|   281k|        _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|   281k|    const uint16_t surrogates_bitmask =
  134|   281k|        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|   281k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (137:9): [True: 272k, False: 8.96k]
  ------------------
  138|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  139|   272k|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  140|   272k|                                              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|   272k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  170|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  171|   272k|      const __m128i t0 = _mm_shuffle_epi8(in, dup_even);
  172|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  173|   272k|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  169|   272k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  174|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  175|   272k|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  169|   272k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  176|       |
  177|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  178|   272k|      const __m128i s0 = _mm_srli_epi16(in, 4);
  179|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  180|   272k|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  169|   272k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  181|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  182|   272k|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  169|   272k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  183|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  184|   272k|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  169|   272k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  185|   272k|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  186|   272k|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  169|   272k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  187|   272k|      const __m128i s4 = _mm_xor_si128(s3, m0);
  188|   272k|#undef simdutf_vec
  189|       |
  190|       |      // 4. expand code units 16-bit => 32-bit
  191|   272k|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  192|   272k|      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|   272k|      const uint16_t mask =
  196|   272k|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  197|   272k|      if (mask == 0) {
  ------------------
  |  Branch (197:11): [True: 119k, False: 153k]
  ------------------
  198|       |        // We only have three-byte code units. Use fast path.
  199|   119k|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  200|   119k|                                              15, 13, -1, -1, -1, -1);
  201|   119k|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  202|   119k|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  203|   119k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  204|   119k|        utf8_output += 12;
  205|   119k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  206|   119k|        utf8_output += 12;
  207|   119k|        buf += 8;
  208|   119k|        continue;
  209|   119k|      }
  210|   153k|      const uint8_t mask0 = uint8_t(mask);
  211|       |
  212|   153k|      const uint8_t *row0 =
  213|   153k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  214|   153k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  215|   153k|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  216|       |
  217|   153k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  218|       |
  219|   153k|      const uint8_t *row1 =
  220|   153k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  221|   153k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  222|   153k|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  223|       |
  224|   153k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  225|   153k|      utf8_output += row0[0];
  226|   153k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  227|   153k|      utf8_output += row1[0];
  228|       |
  229|   153k|      buf += 8;
  230|       |      // surrogate pair(s) in a register
  231|   153k|    } 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|  8.96k|      size_t forward = 15;
  236|  8.96k|      size_t k = 0;
  237|  8.96k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (237:11): [True: 0, False: 8.96k]
  ------------------
  238|      0|        forward = size_t(end - buf - 1);
  239|      0|      }
  240|   132k|      for (; k < forward; k++) {
  ------------------
  |  Branch (240:14): [True: 123k, False: 8.84k]
  ------------------
  241|   123k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  242|   123k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (242:13): [True: 14.2k, False: 109k]
  ------------------
  243|  14.2k|          *utf8_output++ = char(word);
  244|   109k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (244:20): [True: 5.03k, False: 104k]
  ------------------
  245|  5.03k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  246|  5.03k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  247|   104k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (247:20): [True: 93.8k, False: 10.2k]
  ------------------
  248|  93.8k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  249|  93.8k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  250|  93.8k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  251|  93.8k|        } else {
  252|       |          // must be a surrogate pair
  253|  10.2k|          uint16_t diff = uint16_t(word - 0xD800);
  254|  10.2k|          uint16_t next_word =
  255|  10.2k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  256|  10.2k|          k++;
  257|  10.2k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  258|  10.2k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (258:15): [True: 118, False: 10.0k]
  ------------------
  259|    118|            return std::make_pair(nullptr, utf8_output);
  260|    118|          }
  261|  10.0k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  262|  10.0k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  263|  10.0k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  264|  10.0k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  265|  10.0k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  266|  10.0k|        }
  267|   123k|      }
  268|  8.84k|      buf += k;
  269|  8.84k|    }
  270|   281k|  } // while
  271|       |
  272|    756|  return std::make_pair(buf, utf8_output);
  273|    874|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_125sse_convert_utf16_to_utf8ILNS_10endiannessE1EEENSt3__14pairIPKDsPcEES7_mS8_:
   56|    846|sse_convert_utf16_to_utf8(const char16_t *buf, size_t len, char *utf8_output) {
   57|       |
   58|    846|  const char16_t *end = buf + len;
   59|       |
   60|    846|  const __m128i v_0000 = _mm_setzero_si128();
   61|    846|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
   62|    846|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
   63|    846|  const size_t safety_margin =
   64|    846|      12; // to avoid overruns, see issue
   65|       |          // https://github.com/simdutf/simdutf/issues/92
   66|       |
   67|  1.31M|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (67:10): [True: 1.31M, False: 752]
  ------------------
   68|  1.31M|    __m128i in = _mm_loadu_si128((__m128i *)buf);
   69|  1.31M|    if (big_endian) {
  ------------------
  |  Branch (69:9): [True: 1.31M, Folded]
  ------------------
   70|  1.31M|      const __m128i swap =
   71|  1.31M|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   72|  1.31M|      in = _mm_shuffle_epi8(in, swap);
   73|  1.31M|    }
   74|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
   75|  1.31M|    const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80);
   76|  1.31M|    if (_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (76:9): [True: 896k, False: 420k]
  ------------------
   77|   896k|      __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
   78|   896k|      if (big_endian) {
  ------------------
  |  Branch (78:11): [True: 896k, Folded]
  ------------------
   79|   896k|        const __m128i swap =
   80|   896k|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   81|   896k|        nextin = _mm_shuffle_epi8(nextin, swap);
   82|   896k|      }
   83|   896k|      if (!_mm_testz_si128(nextin, v_ff80)) {
  ------------------
  |  Branch (83:11): [True: 48.8k, False: 847k]
  ------------------
   84|       |        // 1. pack the bytes
   85|       |        // obviously suboptimal.
   86|  48.8k|        const __m128i utf8_packed = _mm_packus_epi16(in, in);
   87|       |        // 2. store (16 bytes)
   88|  48.8k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   89|       |        // 3. adjust pointers
   90|  48.8k|        buf += 8;
   91|  48.8k|        utf8_output += 8;
   92|  48.8k|        in = nextin;
   93|   847k|      } else {
   94|       |        // 1. pack the bytes
   95|       |        // obviously suboptimal.
   96|   847k|        const __m128i utf8_packed = _mm_packus_epi16(in, nextin);
   97|       |        // 2. store (16 bytes)
   98|   847k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   99|       |        // 3. adjust pointers
  100|   847k|        buf += 16;
  101|   847k|        utf8_output += 16;
  102|   847k|        continue; // we are done for this round!
  103|   847k|      }
  104|   896k|    }
  105|       |
  106|       |    // no bits set above 7th bit
  107|   468k|    const __m128i one_byte_bytemask =
  108|   468k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000);
  109|   468k|    const uint16_t one_byte_bitmask =
  110|   468k|        static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
  111|       |
  112|       |    // no bits set above 11th bit
  113|   468k|    const __m128i one_or_two_bytes_bytemask =
  114|   468k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000);
  115|   468k|    const uint16_t one_or_two_bytes_bitmask =
  116|   468k|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  117|       |
  118|   468k|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (118:9): [True: 51.7k, False: 417k]
  ------------------
  119|  51.7k|      internal::westmere::write_v_u16_11bits_to_utf8(
  120|  51.7k|          in, utf8_output, one_byte_bytemask, one_byte_bitmask);
  121|  51.7k|      buf += 8;
  122|  51.7k|      continue;
  123|  51.7k|    }
  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|   417k|    const __m128i surrogates_bytemask =
  129|   417k|        _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|   417k|    const uint16_t surrogates_bitmask =
  134|   417k|        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|   417k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (137:9): [True: 405k, False: 11.4k]
  ------------------
  138|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  139|   405k|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  140|   405k|                                              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|   405k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  170|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  171|   405k|      const __m128i t0 = _mm_shuffle_epi8(in, dup_even);
  172|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  173|   405k|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  169|   405k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  174|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  175|   405k|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  169|   405k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  176|       |
  177|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  178|   405k|      const __m128i s0 = _mm_srli_epi16(in, 4);
  179|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  180|   405k|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  169|   405k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  181|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  182|   405k|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  169|   405k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  183|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  184|   405k|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  169|   405k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  185|   405k|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  186|   405k|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  169|   405k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  187|   405k|      const __m128i s4 = _mm_xor_si128(s3, m0);
  188|   405k|#undef simdutf_vec
  189|       |
  190|       |      // 4. expand code units 16-bit => 32-bit
  191|   405k|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  192|   405k|      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|   405k|      const uint16_t mask =
  196|   405k|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  197|   405k|      if (mask == 0) {
  ------------------
  |  Branch (197:11): [True: 207k, False: 198k]
  ------------------
  198|       |        // We only have three-byte code units. Use fast path.
  199|   207k|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  200|   207k|                                              15, 13, -1, -1, -1, -1);
  201|   207k|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  202|   207k|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  203|   207k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  204|   207k|        utf8_output += 12;
  205|   207k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  206|   207k|        utf8_output += 12;
  207|   207k|        buf += 8;
  208|   207k|        continue;
  209|   207k|      }
  210|   198k|      const uint8_t mask0 = uint8_t(mask);
  211|       |
  212|   198k|      const uint8_t *row0 =
  213|   198k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  214|   198k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  215|   198k|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  216|       |
  217|   198k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  218|       |
  219|   198k|      const uint8_t *row1 =
  220|   198k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  221|   198k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  222|   198k|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  223|       |
  224|   198k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  225|   198k|      utf8_output += row0[0];
  226|   198k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  227|   198k|      utf8_output += row1[0];
  228|       |
  229|   198k|      buf += 8;
  230|       |      // surrogate pair(s) in a register
  231|   198k|    } 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|  11.4k|      size_t forward = 15;
  236|  11.4k|      size_t k = 0;
  237|  11.4k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (237:11): [True: 0, False: 11.4k]
  ------------------
  238|      0|        forward = size_t(end - buf - 1);
  239|      0|      }
  240|   170k|      for (; k < forward; k++) {
  ------------------
  |  Branch (240:14): [True: 158k, False: 11.3k]
  ------------------
  241|   158k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  242|   158k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (242:13): [True: 18.2k, False: 140k]
  ------------------
  243|  18.2k|          *utf8_output++ = char(word);
  244|   140k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (244:20): [True: 4.06k, False: 136k]
  ------------------
  245|  4.06k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  246|  4.06k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  247|   136k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (247:20): [True: 123k, False: 12.8k]
  ------------------
  248|   123k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  249|   123k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  250|   123k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  251|   123k|        } else {
  252|       |          // must be a surrogate pair
  253|  12.8k|          uint16_t diff = uint16_t(word - 0xD800);
  254|  12.8k|          uint16_t next_word =
  255|  12.8k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  256|  12.8k|          k++;
  257|  12.8k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  258|  12.8k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (258:15): [True: 94, False: 12.7k]
  ------------------
  259|     94|            return std::make_pair(nullptr, utf8_output);
  260|     94|          }
  261|  12.7k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  262|  12.7k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  263|  12.7k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  264|  12.7k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  265|  12.7k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  266|  12.7k|        }
  267|   158k|      }
  268|  11.3k|      buf += k;
  269|  11.3k|    }
  270|   417k|  } // while
  271|       |
  272|    752|  return std::make_pair(buf, utf8_output);
  273|    846|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_137sse_convert_utf16_to_utf8_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
  285|    686|                                      char *utf8_output) {
  286|    686|  const char16_t *start = buf;
  287|    686|  const char16_t *end = buf + len;
  288|       |
  289|    686|  const __m128i v_0000 = _mm_setzero_si128();
  290|    686|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
  291|    686|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
  292|    686|  const size_t safety_margin =
  293|    686|      12; // to avoid overruns, see issue
  294|       |          // https://github.com/simdutf/simdutf/issues/92
  295|       |
  296|  1.13M|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (296:10): [True: 1.13M, False: 592]
  ------------------
  297|  1.13M|    __m128i in = _mm_loadu_si128((__m128i *)buf);
  298|  1.13M|    if (big_endian) {
  ------------------
  |  Branch (298:9): [Folded, False: 1.13M]
  ------------------
  299|      0|      const __m128i swap =
  300|      0|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  301|      0|      in = _mm_shuffle_epi8(in, swap);
  302|      0|    }
  303|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
  304|  1.13M|    const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80);
  305|  1.13M|    if (_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (305:9): [True: 330k, False: 802k]
  ------------------
  306|   330k|      __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
  307|   330k|      if (big_endian) {
  ------------------
  |  Branch (307:11): [Folded, False: 330k]
  ------------------
  308|      0|        const __m128i swap =
  309|      0|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  310|      0|        nextin = _mm_shuffle_epi8(nextin, swap);
  311|      0|      }
  312|   330k|      if (!_mm_testz_si128(nextin, v_ff80)) {
  ------------------
  |  Branch (312:11): [True: 21.2k, False: 309k]
  ------------------
  313|       |        // 1. pack the bytes
  314|       |        // obviously suboptimal.
  315|  21.2k|        const __m128i utf8_packed = _mm_packus_epi16(in, in);
  316|       |        // 2. store (16 bytes)
  317|  21.2k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  318|       |        // 3. adjust pointers
  319|  21.2k|        buf += 8;
  320|  21.2k|        utf8_output += 8;
  321|  21.2k|        in = nextin;
  322|   309k|      } else {
  323|       |        // 1. pack the bytes
  324|       |        // obviously suboptimal.
  325|   309k|        const __m128i utf8_packed = _mm_packus_epi16(in, nextin);
  326|       |        // 2. store (16 bytes)
  327|   309k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  328|       |        // 3. adjust pointers
  329|   309k|        buf += 16;
  330|   309k|        utf8_output += 16;
  331|   309k|        continue; // we are done for this round!
  332|   309k|      }
  333|   330k|    }
  334|       |
  335|       |    // no bits set above 7th bit
  336|   823k|    const __m128i one_byte_bytemask =
  337|   823k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000);
  338|   823k|    const uint16_t one_byte_bitmask =
  339|   823k|        static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
  340|       |
  341|       |    // no bits set above 11th bit
  342|   823k|    const __m128i one_or_two_bytes_bytemask =
  343|   823k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000);
  344|   823k|    const uint16_t one_or_two_bytes_bitmask =
  345|   823k|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  346|       |
  347|   823k|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (347:9): [True: 103k, False: 719k]
  ------------------
  348|   103k|      internal::westmere::write_v_u16_11bits_to_utf8(
  349|   103k|          in, utf8_output, one_byte_bytemask, one_byte_bitmask);
  350|   103k|      buf += 8;
  351|   103k|      continue;
  352|   103k|    }
  353|       |
  354|       |    // 1. Check if there are any surrogate word in the input chunk.
  355|       |    //    We have also deal with situation when there is a surrogate word
  356|       |    //    at the end of a chunk.
  357|   719k|    const __m128i surrogates_bytemask =
  358|   719k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800);
  359|       |
  360|       |    // bitmask = 0x0000 if there are no surrogates
  361|       |    //         = 0xc000 if the last word is a surrogate
  362|   719k|    const uint16_t surrogates_bitmask =
  363|   719k|        static_cast<uint16_t>(_mm_movemask_epi8(surrogates_bytemask));
  364|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
  365|       |    // However, it is likely an uncommon occurrence.
  366|   719k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (366:9): [True: 714k, False: 5.17k]
  ------------------
  367|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  368|   714k|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  369|   714k|                                              0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
  370|       |
  371|       |      /* In this branch we handle three cases:
  372|       |         1. [0000|0000|0ccc|cccc] => [0ccc|cccc]                           -
  373|       |        single UFT-8 byte
  374|       |         2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc]              - two
  375|       |        UTF-8 bytes
  376|       |         3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] -
  377|       |        three UTF-8 bytes
  378|       |
  379|       |        We expand the input word (16-bit) into two code units (32-bit), thus
  380|       |        we have room for four bytes. However, we need five distinct bit
  381|       |        layouts. Note that the last byte in cases #2 and #3 is the same.
  382|       |
  383|       |        We precompute byte 1 for case #1 and the common byte for cases #2 & #3
  384|       |        in register t2.
  385|       |
  386|       |        We precompute byte 1 for case #3 and -- **conditionally** -- precompute
  387|       |        either byte 1 for case #2 or byte 2 for case #3. Note that they
  388|       |        differ by exactly one bit.
  389|       |
  390|       |        Finally from these two code units we build proper UTF-8 sequence, taking
  391|       |        into account the case (i.e, the number of bytes to write).
  392|       |      */
  393|       |      /**
  394|       |       * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
  395|       |       * t2 => [0ccc|cccc] [10cc|cccc]
  396|       |       * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
  397|       |       */
  398|   714k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  399|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  400|   714k|      const __m128i t0 = _mm_shuffle_epi8(in, dup_even);
  401|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  402|   714k|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  398|   714k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  403|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  404|   714k|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  398|   714k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  405|       |
  406|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  407|   714k|      const __m128i s0 = _mm_srli_epi16(in, 4);
  408|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  409|   714k|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  398|   714k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  410|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  411|   714k|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  398|   714k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  412|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  413|   714k|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  398|   714k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  414|   714k|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  415|   714k|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  398|   714k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  416|   714k|      const __m128i s4 = _mm_xor_si128(s3, m0);
  417|   714k|#undef simdutf_vec
  418|       |
  419|       |      // 4. expand code units 16-bit => 32-bit
  420|   714k|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  421|   714k|      const __m128i out1 = _mm_unpackhi_epi16(t2, s4);
  422|       |
  423|       |      // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
  424|   714k|      const uint16_t mask =
  425|   714k|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  426|   714k|      if (mask == 0) {
  ------------------
  |  Branch (426:11): [True: 585k, False: 128k]
  ------------------
  427|       |        // We only have three-byte code units. Use fast path.
  428|   585k|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  429|   585k|                                              15, 13, -1, -1, -1, -1);
  430|   585k|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  431|   585k|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  432|   585k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  433|   585k|        utf8_output += 12;
  434|   585k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  435|   585k|        utf8_output += 12;
  436|   585k|        buf += 8;
  437|   585k|        continue;
  438|   585k|      }
  439|   128k|      const uint8_t mask0 = uint8_t(mask);
  440|       |
  441|   128k|      const uint8_t *row0 =
  442|   128k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  443|   128k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  444|   128k|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  445|       |
  446|   128k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  447|       |
  448|   128k|      const uint8_t *row1 =
  449|   128k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  450|   128k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  451|   128k|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  452|       |
  453|   128k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  454|   128k|      utf8_output += row0[0];
  455|   128k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  456|   128k|      utf8_output += row1[0];
  457|       |
  458|   128k|      buf += 8;
  459|       |      // surrogate pair(s) in a register
  460|   128k|    } else {
  461|       |      // Let us do a scalar fallback.
  462|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
  463|       |      // in the presence of surrogate pairs may require non-trivial tables.
  464|  5.17k|      size_t forward = 15;
  465|  5.17k|      size_t k = 0;
  466|  5.17k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (466:11): [True: 0, False: 5.17k]
  ------------------
  467|      0|        forward = size_t(end - buf - 1);
  468|      0|      }
  469|  75.8k|      for (; k < forward; k++) {
  ------------------
  |  Branch (469:14): [True: 70.7k, False: 5.07k]
  ------------------
  470|  70.7k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  471|  70.7k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (471:13): [True: 9.66k, False: 61.0k]
  ------------------
  472|  9.66k|          *utf8_output++ = char(word);
  473|  61.0k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (473:20): [True: 3.02k, False: 58.0k]
  ------------------
  474|  3.02k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  475|  3.02k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  476|  58.0k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (476:20): [True: 51.9k, False: 6.12k]
  ------------------
  477|  51.9k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  478|  51.9k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  479|  51.9k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  480|  51.9k|        } else {
  481|       |          // must be a surrogate pair
  482|  6.12k|          uint16_t diff = uint16_t(word - 0xD800);
  483|  6.12k|          uint16_t next_word =
  484|  6.12k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  485|  6.12k|          k++;
  486|  6.12k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  487|  6.12k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (487:15): [True: 94, False: 6.03k]
  ------------------
  488|     94|            return std::make_pair(
  489|     94|                result(error_code::SURROGATE, buf - start + k - 1),
  490|     94|                utf8_output);
  491|     94|          }
  492|  6.03k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  493|  6.03k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  494|  6.03k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  495|  6.03k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  496|  6.03k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  497|  6.03k|        }
  498|  70.7k|      }
  499|  5.07k|      buf += k;
  500|  5.07k|    }
  501|   719k|  } // while
  502|       |
  503|    592|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  504|    686|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_137sse_convert_utf16_to_utf8_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
  285|    556|                                      char *utf8_output) {
  286|    556|  const char16_t *start = buf;
  287|    556|  const char16_t *end = buf + len;
  288|       |
  289|    556|  const __m128i v_0000 = _mm_setzero_si128();
  290|    556|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
  291|    556|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
  292|    556|  const size_t safety_margin =
  293|    556|      12; // to avoid overruns, see issue
  294|       |          // https://github.com/simdutf/simdutf/issues/92
  295|       |
  296|   450k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (296:10): [True: 449k, False: 428]
  ------------------
  297|   449k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
  298|   449k|    if (big_endian) {
  ------------------
  |  Branch (298:9): [True: 449k, Folded]
  ------------------
  299|   449k|      const __m128i swap =
  300|   449k|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  301|   449k|      in = _mm_shuffle_epi8(in, swap);
  302|   449k|    }
  303|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
  304|   449k|    const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80);
  305|   449k|    if (_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (305:9): [True: 335k, False: 113k]
  ------------------
  306|   335k|      __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
  307|   335k|      if (big_endian) {
  ------------------
  |  Branch (307:11): [True: 335k, Folded]
  ------------------
  308|   335k|        const __m128i swap =
  309|   335k|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  310|   335k|        nextin = _mm_shuffle_epi8(nextin, swap);
  311|   335k|      }
  312|   335k|      if (!_mm_testz_si128(nextin, v_ff80)) {
  ------------------
  |  Branch (312:11): [True: 34.0k, False: 301k]
  ------------------
  313|       |        // 1. pack the bytes
  314|       |        // obviously suboptimal.
  315|  34.0k|        const __m128i utf8_packed = _mm_packus_epi16(in, in);
  316|       |        // 2. store (16 bytes)
  317|  34.0k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  318|       |        // 3. adjust pointers
  319|  34.0k|        buf += 8;
  320|  34.0k|        utf8_output += 8;
  321|  34.0k|        in = nextin;
  322|   301k|      } else {
  323|       |        // 1. pack the bytes
  324|       |        // obviously suboptimal.
  325|   301k|        const __m128i utf8_packed = _mm_packus_epi16(in, nextin);
  326|       |        // 2. store (16 bytes)
  327|   301k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  328|       |        // 3. adjust pointers
  329|   301k|        buf += 16;
  330|   301k|        utf8_output += 16;
  331|   301k|        continue; // we are done for this round!
  332|   301k|      }
  333|   335k|    }
  334|       |
  335|       |    // no bits set above 7th bit
  336|   147k|    const __m128i one_byte_bytemask =
  337|   147k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000);
  338|   147k|    const uint16_t one_byte_bitmask =
  339|   147k|        static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
  340|       |
  341|       |    // no bits set above 11th bit
  342|   147k|    const __m128i one_or_two_bytes_bytemask =
  343|   147k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000);
  344|   147k|    const uint16_t one_or_two_bytes_bitmask =
  345|   147k|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  346|       |
  347|   147k|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (347:9): [True: 42.1k, False: 105k]
  ------------------
  348|  42.1k|      internal::westmere::write_v_u16_11bits_to_utf8(
  349|  42.1k|          in, utf8_output, one_byte_bytemask, one_byte_bitmask);
  350|  42.1k|      buf += 8;
  351|  42.1k|      continue;
  352|  42.1k|    }
  353|       |
  354|       |    // 1. Check if there are any surrogate word in the input chunk.
  355|       |    //    We have also deal with situation when there is a surrogate word
  356|       |    //    at the end of a chunk.
  357|   105k|    const __m128i surrogates_bytemask =
  358|   105k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800);
  359|       |
  360|       |    // bitmask = 0x0000 if there are no surrogates
  361|       |    //         = 0xc000 if the last word is a surrogate
  362|   105k|    const uint16_t surrogates_bitmask =
  363|   105k|        static_cast<uint16_t>(_mm_movemask_epi8(surrogates_bytemask));
  364|       |    // It might seem like checking for surrogates_bitmask == 0xc000 could help.
  365|       |    // However, it is likely an uncommon occurrence.
  366|   105k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (366:9): [True: 98.8k, False: 6.97k]
  ------------------
  367|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  368|  98.8k|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  369|  98.8k|                                              0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
  370|       |
  371|       |      /* In this branch we handle three cases:
  372|       |         1. [0000|0000|0ccc|cccc] => [0ccc|cccc]                           -
  373|       |        single UFT-8 byte
  374|       |         2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc]              - two
  375|       |        UTF-8 bytes
  376|       |         3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] -
  377|       |        three UTF-8 bytes
  378|       |
  379|       |        We expand the input word (16-bit) into two code units (32-bit), thus
  380|       |        we have room for four bytes. However, we need five distinct bit
  381|       |        layouts. Note that the last byte in cases #2 and #3 is the same.
  382|       |
  383|       |        We precompute byte 1 for case #1 and the common byte for cases #2 & #3
  384|       |        in register t2.
  385|       |
  386|       |        We precompute byte 1 for case #3 and -- **conditionally** -- precompute
  387|       |        either byte 1 for case #2 or byte 2 for case #3. Note that they
  388|       |        differ by exactly one bit.
  389|       |
  390|       |        Finally from these two code units we build proper UTF-8 sequence, taking
  391|       |        into account the case (i.e, the number of bytes to write).
  392|       |      */
  393|       |      /**
  394|       |       * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
  395|       |       * t2 => [0ccc|cccc] [10cc|cccc]
  396|       |       * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
  397|       |       */
  398|  98.8k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  399|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  400|  98.8k|      const __m128i t0 = _mm_shuffle_epi8(in, dup_even);
  401|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  402|  98.8k|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  398|  98.8k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  403|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  404|  98.8k|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  398|  98.8k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  405|       |
  406|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  407|  98.8k|      const __m128i s0 = _mm_srli_epi16(in, 4);
  408|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  409|  98.8k|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  398|  98.8k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  410|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  411|  98.8k|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  398|  98.8k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  412|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  413|  98.8k|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  398|  98.8k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  414|  98.8k|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  415|  98.8k|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  398|  98.8k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  416|  98.8k|      const __m128i s4 = _mm_xor_si128(s3, m0);
  417|  98.8k|#undef simdutf_vec
  418|       |
  419|       |      // 4. expand code units 16-bit => 32-bit
  420|  98.8k|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  421|  98.8k|      const __m128i out1 = _mm_unpackhi_epi16(t2, s4);
  422|       |
  423|       |      // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
  424|  98.8k|      const uint16_t mask =
  425|  98.8k|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  426|  98.8k|      if (mask == 0) {
  ------------------
  |  Branch (426:11): [True: 24.6k, False: 74.2k]
  ------------------
  427|       |        // We only have three-byte code units. Use fast path.
  428|  24.6k|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  429|  24.6k|                                              15, 13, -1, -1, -1, -1);
  430|  24.6k|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  431|  24.6k|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  432|  24.6k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  433|  24.6k|        utf8_output += 12;
  434|  24.6k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  435|  24.6k|        utf8_output += 12;
  436|  24.6k|        buf += 8;
  437|  24.6k|        continue;
  438|  24.6k|      }
  439|  74.2k|      const uint8_t mask0 = uint8_t(mask);
  440|       |
  441|  74.2k|      const uint8_t *row0 =
  442|  74.2k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  443|  74.2k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  444|  74.2k|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  445|       |
  446|  74.2k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  447|       |
  448|  74.2k|      const uint8_t *row1 =
  449|  74.2k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  450|  74.2k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  451|  74.2k|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  452|       |
  453|  74.2k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  454|  74.2k|      utf8_output += row0[0];
  455|  74.2k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  456|  74.2k|      utf8_output += row1[0];
  457|       |
  458|  74.2k|      buf += 8;
  459|       |      // surrogate pair(s) in a register
  460|  74.2k|    } else {
  461|       |      // Let us do a scalar fallback.
  462|       |      // It may seem wasteful to use scalar code, but being efficient with SIMD
  463|       |      // in the presence of surrogate pairs may require non-trivial tables.
  464|  6.97k|      size_t forward = 15;
  465|  6.97k|      size_t k = 0;
  466|  6.97k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (466:11): [True: 0, False: 6.97k]
  ------------------
  467|      0|        forward = size_t(end - buf - 1);
  468|      0|      }
  469|   102k|      for (; k < forward; k++) {
  ------------------
  |  Branch (469:14): [True: 95.5k, False: 6.84k]
  ------------------
  470|  95.5k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  471|  95.5k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (471:13): [True: 35.3k, False: 60.2k]
  ------------------
  472|  35.3k|          *utf8_output++ = char(word);
  473|  60.2k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (473:20): [True: 4.12k, False: 56.0k]
  ------------------
  474|  4.12k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  475|  4.12k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  476|  56.0k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (476:20): [True: 48.1k, False: 7.91k]
  ------------------
  477|  48.1k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  478|  48.1k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  479|  48.1k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  480|  48.1k|        } else {
  481|       |          // must be a surrogate pair
  482|  7.91k|          uint16_t diff = uint16_t(word - 0xD800);
  483|  7.91k|          uint16_t next_word =
  484|  7.91k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  485|  7.91k|          k++;
  486|  7.91k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  487|  7.91k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (487:15): [True: 128, False: 7.78k]
  ------------------
  488|    128|            return std::make_pair(
  489|    128|                result(error_code::SURROGATE, buf - start + k - 1),
  490|    128|                utf8_output);
  491|    128|          }
  492|  7.78k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  493|  7.78k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  494|  7.78k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  495|  7.78k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  496|  7.78k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  497|  7.78k|        }
  498|  95.5k|      }
  499|  6.84k|      buf += k;
  500|  6.84k|    }
  501|   105k|  } // while
  502|       |
  503|    428|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  504|    556|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_127sse_convert_utf32_to_latin1EPKDimPc:
    3|    186|                            char *latin1_output) {
    4|    186|  const size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16
    5|       |
    6|    186|  __m128i high_bytes_mask = _mm_set1_epi32(0xFFFFFF00);
    7|    186|  __m128i shufmask =
    8|    186|      _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 8, 4, 0);
    9|       |
   10|  1.40k|  for (size_t i = 0; i < rounded_len; i += 16) {
  ------------------
  |  Branch (10:22): [True: 1.25k, False: 150]
  ------------------
   11|  1.25k|    __m128i in1 = _mm_loadu_si128((__m128i *)buf);
   12|  1.25k|    __m128i in2 = _mm_loadu_si128((__m128i *)(buf + 4));
   13|  1.25k|    __m128i in3 = _mm_loadu_si128((__m128i *)(buf + 8));
   14|  1.25k|    __m128i in4 = _mm_loadu_si128((__m128i *)(buf + 12));
   15|       |
   16|  1.25k|    __m128i check_combined = _mm_or_si128(in1, in2);
   17|  1.25k|    check_combined = _mm_or_si128(check_combined, in3);
   18|  1.25k|    check_combined = _mm_or_si128(check_combined, in4);
   19|       |
   20|  1.25k|    if (!_mm_testz_si128(check_combined, high_bytes_mask)) {
  ------------------
  |  Branch (20:9): [True: 36, False: 1.22k]
  ------------------
   21|     36|      return std::make_pair(nullptr, latin1_output);
   22|     36|    }
   23|  1.22k|    __m128i pack1 = _mm_unpacklo_epi32(_mm_shuffle_epi8(in1, shufmask),
   24|  1.22k|                                       _mm_shuffle_epi8(in2, shufmask));
   25|  1.22k|    __m128i pack2 = _mm_unpacklo_epi32(_mm_shuffle_epi8(in3, shufmask),
   26|  1.22k|                                       _mm_shuffle_epi8(in4, shufmask));
   27|  1.22k|    __m128i pack = _mm_unpacklo_epi64(pack1, pack2);
   28|  1.22k|    _mm_storeu_si128((__m128i *)latin1_output, pack);
   29|  1.22k|    latin1_output += 16;
   30|  1.22k|    buf += 16;
   31|  1.22k|  }
   32|       |
   33|    150|  return std::make_pair(buf, latin1_output);
   34|    186|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_139sse_convert_utf32_to_latin1_with_errorsEPKDimPc:
   38|    420|                                        char *latin1_output) {
   39|    420|  const char32_t *start = buf;
   40|    420|  const size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16
   41|       |
   42|    420|  __m128i high_bytes_mask = _mm_set1_epi32(0xFFFFFF00);
   43|    420|  __m128i shufmask =
   44|    420|      _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 8, 4, 0);
   45|       |
   46|  18.9k|  for (size_t i = 0; i < rounded_len; i += 16) {
  ------------------
  |  Branch (46:22): [True: 18.7k, False: 266]
  ------------------
   47|  18.7k|    __m128i in1 = _mm_loadu_si128((__m128i *)buf);
   48|  18.7k|    __m128i in2 = _mm_loadu_si128((__m128i *)(buf + 4));
   49|  18.7k|    __m128i in3 = _mm_loadu_si128((__m128i *)(buf + 8));
   50|  18.7k|    __m128i in4 = _mm_loadu_si128((__m128i *)(buf + 12));
   51|       |
   52|  18.7k|    __m128i check_combined = _mm_or_si128(in1, in2);
   53|  18.7k|    check_combined = _mm_or_si128(check_combined, in3);
   54|  18.7k|    check_combined = _mm_or_si128(check_combined, in4);
   55|       |
   56|  18.7k|    if (!_mm_testz_si128(check_combined, high_bytes_mask)) {
  ------------------
  |  Branch (56:9): [True: 154, False: 18.5k]
  ------------------
   57|       |      // Fallback to scalar code for handling errors
   58|    582|      for (int k = 0; k < 16; k++) {
  ------------------
  |  Branch (58:23): [True: 582, False: 0]
  ------------------
   59|    582|        char32_t codepoint = buf[k];
   60|    582|        if (codepoint <= 0xff) {
  ------------------
  |  Branch (60:13): [True: 428, False: 154]
  ------------------
   61|    428|          *latin1_output++ = char(codepoint);
   62|    428|        } else {
   63|    154|          return std::make_pair(result(error_code::TOO_LARGE, buf - start + k),
   64|    154|                                latin1_output);
   65|    154|        }
   66|    582|      }
   67|      0|      buf += 16;
   68|      0|      continue;
   69|    154|    }
   70|  18.5k|    __m128i pack1 = _mm_unpacklo_epi32(_mm_shuffle_epi8(in1, shufmask),
   71|  18.5k|                                       _mm_shuffle_epi8(in2, shufmask));
   72|  18.5k|    __m128i pack2 = _mm_unpacklo_epi32(_mm_shuffle_epi8(in3, shufmask),
   73|  18.5k|                                       _mm_shuffle_epi8(in4, shufmask));
   74|  18.5k|    __m128i pack = _mm_unpacklo_epi64(pack1, pack2);
   75|  18.5k|    _mm_storeu_si128((__m128i *)latin1_output, pack);
   76|  18.5k|    latin1_output += 16;
   77|  18.5k|    buf += 16;
   78|  18.5k|  }
   79|       |
   80|    266|  return std::make_pair(result(error_code::SUCCESS, buf - start),
   81|    266|                        latin1_output);
   82|    420|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_126sse_convert_utf32_to_utf16ILNS_10endiannessE0EEENSt3__14pairIPKDiPDsEES7_mS8_:
   58|    806|                           char16_t *utf16_output) {
   59|       |
   60|    806|  const char32_t *end = buf + len;
   61|       |
   62|    806|  const __m128i v_ffff0000 = _mm_set1_epi32((int32_t)0xffff0000);
   63|    806|  __m128i forbidden_bytemask = _mm_setzero_si128();
   64|       |
   65|  60.4k|  while (end - buf >= 16 + 8) {
  ------------------
  |  Branch (65:10): [True: 59.7k, False: 752]
  ------------------
   66|  59.7k|    const __m128i *ptr = reinterpret_cast<const __m128i *>(buf);
   67|  59.7k|    const __m128i in0 = _mm_loadu_si128(ptr + 0);
   68|  59.7k|    const __m128i in1 = _mm_loadu_si128(ptr + 1);
   69|  59.7k|    const __m128i in2 = _mm_loadu_si128(ptr + 2);
   70|  59.7k|    const __m128i in3 = _mm_loadu_si128(ptr + 3);
   71|       |
   72|  59.7k|    const __m128i combined =
   73|  59.7k|        _mm_or_si128(_mm_or_si128(in2, in3), _mm_or_si128(in0, in1));
   74|  59.7k|    if (simdutf_likely(_mm_testz_si128(combined, v_ffff0000))) {
  ------------------
  |  |   90|  59.7k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (90:31): [True: 58.2k, False: 1.53k]
  |  |  ------------------
  ------------------
   75|       |      // No bits set above 16th, directly pack UTF-32 to UTF-16
   76|  58.2k|      __m128i utf16_packed0 = _mm_packus_epi32(in0, in1);
   77|  58.2k|      __m128i utf16_packed1 = _mm_packus_epi32(in2, in3);
   78|       |
   79|  58.2k|      const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800);
   80|  58.2k|      const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
   81|  58.2k|      forbidden_bytemask = _mm_or_si128(
   82|  58.2k|          forbidden_bytemask,
   83|  58.2k|          _mm_or_si128(
   84|  58.2k|              _mm_cmpeq_epi16(_mm_and_si128(utf16_packed0, v_f800), v_d800),
   85|  58.2k|              _mm_cmpeq_epi16(_mm_and_si128(utf16_packed1, v_f800), v_d800)));
   86|       |
   87|  58.2k|      if (big_endian) {
  ------------------
  |  Branch (87:11): [Folded, False: 58.2k]
  ------------------
   88|      0|        const __m128i swap =
   89|      0|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   90|      0|        utf16_packed0 = _mm_shuffle_epi8(utf16_packed0, swap);
   91|      0|        utf16_packed1 = _mm_shuffle_epi8(utf16_packed1, swap);
   92|      0|      }
   93|       |
   94|  58.2k|      _mm_storeu_si128((__m128i *)utf16_output + 0, utf16_packed0);
   95|  58.2k|      _mm_storeu_si128((__m128i *)utf16_output + 1, utf16_packed1);
   96|  58.2k|      utf16_output += 16;
   97|  58.2k|      buf += 16;
   98|  58.2k|    } else {
   99|  1.53k|      if (!validate_utf32(in0, in1) || !validate_utf32(in2, in3)) {
  ------------------
  |  Branch (99:11): [True: 28, False: 1.50k]
  |  Branch (99:40): [True: 26, False: 1.47k]
  ------------------
  100|     54|        return std::make_pair(nullptr, utf16_output);
  101|     54|      }
  102|       |
  103|  1.47k|      const auto ret0 = sse_expand_surrogate<big_endian>(in0);
  104|  1.47k|      _mm_storeu_si128((__m128i *)utf16_output, ret0.compressed);
  105|  1.47k|      utf16_output += ret0.u16count;
  106|       |
  107|  1.47k|      const auto ret1 = sse_expand_surrogate<big_endian>(in1);
  108|  1.47k|      _mm_storeu_si128((__m128i *)utf16_output, ret1.compressed);
  109|  1.47k|      utf16_output += ret1.u16count;
  110|       |
  111|  1.47k|      const auto ret2 = sse_expand_surrogate<big_endian>(in2);
  112|  1.47k|      _mm_storeu_si128((__m128i *)utf16_output, ret2.compressed);
  113|  1.47k|      utf16_output += ret2.u16count;
  114|       |
  115|  1.47k|      const auto ret3 = sse_expand_surrogate<big_endian>(in3);
  116|  1.47k|      _mm_storeu_si128((__m128i *)utf16_output, ret3.compressed);
  117|  1.47k|      utf16_output += ret3.u16count;
  118|       |
  119|  1.47k|      buf += 16;
  120|  1.47k|    }
  121|  59.7k|  }
  122|       |
  123|       |  // check for invalid input
  124|    752|  if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (124:7): [True: 4, False: 748]
  ------------------
  125|      4|    return std::make_pair(nullptr, utf16_output);
  126|      4|  }
  127|       |
  128|    748|  return std::make_pair(buf, utf16_output);
  129|    752|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_114validate_utf32EDv2_xS2_:
   37|  5.58k|simdutf_really_inline bool validate_utf32(const __m128i a, const __m128i b) {
   38|  5.58k|  using vector_u32 = simd32<uint32_t>;
   39|       |
   40|  5.58k|  const auto in0 = vector_u32(a);
   41|  5.58k|  const auto in1 = vector_u32(b);
   42|       |
   43|  5.58k|  const auto standardmax = vector_u32::splat(0x10ffff);
   44|  5.58k|  const auto offset = vector_u32::splat(0xffff2000);
   45|  5.58k|  const auto standardoffsetmax = vector_u32::splat(0xfffff7ff);
   46|       |
   47|  5.58k|  const auto too_large = max(in0, in1) > standardmax;
   48|  5.58k|  const auto surrogate0 = (in0 + offset) > standardoffsetmax;
   49|  5.58k|  const auto surrogate1 = (in1 + offset) > standardoffsetmax;
   50|       |
   51|  5.58k|  const auto combined = too_large | surrogate0 | surrogate1;
   52|  5.58k|  return !combined.any();
   53|  5.58k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_120sse_expand_surrogateILNS_10endiannessE0EEENS1_18expansion_result_tEDv2_x:
    9|  5.91k|expansion_result_t sse_expand_surrogate(const __m128i x) {
   10|  5.91k|  using vector_u32 = simd32<uint32_t>;
   11|  5.91k|  using vector_u8 = simd8<uint8_t>;
   12|       |
   13|  5.91k|  const auto in = vector_u32(x);
   14|       |
   15|  5.91k|  const auto non_surrogate_mask = (in & uint32_t(0xffff0000)) == uint32_t(0);
   16|  5.91k|  const auto mask = (~non_surrogate_mask.to_4bit_bitmask()) & 0xf;
   17|       |
   18|  5.91k|  const auto t0 = in - uint32_t(0x00010000);
   19|  5.91k|  const auto hi = t0.shr<10>() & uint32_t(0x000003ff);
   20|  5.91k|  const auto lo = t0.shl<16>() & uint32_t(0x03ff0000);
   21|  5.91k|  const auto surrogates = (lo | hi) | uint32_t(0xdc00d800);
   22|       |
   23|  5.91k|  const auto merged = as_vector_u8(select(non_surrogate_mask, in, surrogates));
   24|       |
   25|  5.91k|  const auto shuffle = vector_u8::load(
   26|  5.91k|      (byte_order == endianness::LITTLE)
  ------------------
  |  Branch (26:7): [True: 5.91k, Folded]
  ------------------
   27|  5.91k|          ? tables::utf32_to_utf16::pack_utf32_to_utf16le[mask]
   28|  5.91k|          : tables::utf32_to_utf16::pack_utf32_to_utf16be[mask]);
   29|       |
   30|  5.91k|  const size_t u16count = (4 + count_ones(mask));
   31|  5.91k|  const auto compressed = shuffle.lookup_16(merged);
   32|       |
   33|  5.91k|  return {u16count, compressed};
   34|  5.91k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_126sse_convert_utf32_to_utf16ILNS_10endiannessE1EEENSt3__14pairIPKDiPDsEES7_mS8_:
   58|    620|                           char16_t *utf16_output) {
   59|       |
   60|    620|  const char32_t *end = buf + len;
   61|       |
   62|    620|  const __m128i v_ffff0000 = _mm_set1_epi32((int32_t)0xffff0000);
   63|    620|  __m128i forbidden_bytemask = _mm_setzero_si128();
   64|       |
   65|  3.97k|  while (end - buf >= 16 + 8) {
  ------------------
  |  Branch (65:10): [True: 3.40k, False: 568]
  ------------------
   66|  3.40k|    const __m128i *ptr = reinterpret_cast<const __m128i *>(buf);
   67|  3.40k|    const __m128i in0 = _mm_loadu_si128(ptr + 0);
   68|  3.40k|    const __m128i in1 = _mm_loadu_si128(ptr + 1);
   69|  3.40k|    const __m128i in2 = _mm_loadu_si128(ptr + 2);
   70|  3.40k|    const __m128i in3 = _mm_loadu_si128(ptr + 3);
   71|       |
   72|  3.40k|    const __m128i combined =
   73|  3.40k|        _mm_or_si128(_mm_or_si128(in2, in3), _mm_or_si128(in0, in1));
   74|  3.40k|    if (simdutf_likely(_mm_testz_si128(combined, v_ffff0000))) {
  ------------------
  |  |   90|  3.40k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (90:31): [True: 2.11k, False: 1.29k]
  |  |  ------------------
  ------------------
   75|       |      // No bits set above 16th, directly pack UTF-32 to UTF-16
   76|  2.11k|      __m128i utf16_packed0 = _mm_packus_epi32(in0, in1);
   77|  2.11k|      __m128i utf16_packed1 = _mm_packus_epi32(in2, in3);
   78|       |
   79|  2.11k|      const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800);
   80|  2.11k|      const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
   81|  2.11k|      forbidden_bytemask = _mm_or_si128(
   82|  2.11k|          forbidden_bytemask,
   83|  2.11k|          _mm_or_si128(
   84|  2.11k|              _mm_cmpeq_epi16(_mm_and_si128(utf16_packed0, v_f800), v_d800),
   85|  2.11k|              _mm_cmpeq_epi16(_mm_and_si128(utf16_packed1, v_f800), v_d800)));
   86|       |
   87|  2.11k|      if (big_endian) {
  ------------------
  |  Branch (87:11): [True: 2.11k, Folded]
  ------------------
   88|  2.11k|        const __m128i swap =
   89|  2.11k|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   90|  2.11k|        utf16_packed0 = _mm_shuffle_epi8(utf16_packed0, swap);
   91|  2.11k|        utf16_packed1 = _mm_shuffle_epi8(utf16_packed1, swap);
   92|  2.11k|      }
   93|       |
   94|  2.11k|      _mm_storeu_si128((__m128i *)utf16_output + 0, utf16_packed0);
   95|  2.11k|      _mm_storeu_si128((__m128i *)utf16_output + 1, utf16_packed1);
   96|  2.11k|      utf16_output += 16;
   97|  2.11k|      buf += 16;
   98|  2.11k|    } else {
   99|  1.29k|      if (!validate_utf32(in0, in1) || !validate_utf32(in2, in3)) {
  ------------------
  |  Branch (99:11): [True: 28, False: 1.26k]
  |  Branch (99:40): [True: 24, False: 1.23k]
  ------------------
  100|     52|        return std::make_pair(nullptr, utf16_output);
  101|     52|      }
  102|       |
  103|  1.23k|      const auto ret0 = sse_expand_surrogate<big_endian>(in0);
  104|  1.23k|      _mm_storeu_si128((__m128i *)utf16_output, ret0.compressed);
  105|  1.23k|      utf16_output += ret0.u16count;
  106|       |
  107|  1.23k|      const auto ret1 = sse_expand_surrogate<big_endian>(in1);
  108|  1.23k|      _mm_storeu_si128((__m128i *)utf16_output, ret1.compressed);
  109|  1.23k|      utf16_output += ret1.u16count;
  110|       |
  111|  1.23k|      const auto ret2 = sse_expand_surrogate<big_endian>(in2);
  112|  1.23k|      _mm_storeu_si128((__m128i *)utf16_output, ret2.compressed);
  113|  1.23k|      utf16_output += ret2.u16count;
  114|       |
  115|  1.23k|      const auto ret3 = sse_expand_surrogate<big_endian>(in3);
  116|  1.23k|      _mm_storeu_si128((__m128i *)utf16_output, ret3.compressed);
  117|  1.23k|      utf16_output += ret3.u16count;
  118|       |
  119|  1.23k|      buf += 16;
  120|  1.23k|    }
  121|  3.40k|  }
  122|       |
  123|       |  // check for invalid input
  124|    568|  if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (124:7): [True: 16, False: 552]
  ------------------
  125|     16|    return std::make_pair(nullptr, utf16_output);
  126|     16|  }
  127|       |
  128|    552|  return std::make_pair(buf, utf16_output);
  129|    568|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_120sse_expand_surrogateILNS_10endiannessE1EEENS1_18expansion_result_tEDv2_x:
    9|  4.95k|expansion_result_t sse_expand_surrogate(const __m128i x) {
   10|  4.95k|  using vector_u32 = simd32<uint32_t>;
   11|  4.95k|  using vector_u8 = simd8<uint8_t>;
   12|       |
   13|  4.95k|  const auto in = vector_u32(x);
   14|       |
   15|  4.95k|  const auto non_surrogate_mask = (in & uint32_t(0xffff0000)) == uint32_t(0);
   16|  4.95k|  const auto mask = (~non_surrogate_mask.to_4bit_bitmask()) & 0xf;
   17|       |
   18|  4.95k|  const auto t0 = in - uint32_t(0x00010000);
   19|  4.95k|  const auto hi = t0.shr<10>() & uint32_t(0x000003ff);
   20|  4.95k|  const auto lo = t0.shl<16>() & uint32_t(0x03ff0000);
   21|  4.95k|  const auto surrogates = (lo | hi) | uint32_t(0xdc00d800);
   22|       |
   23|  4.95k|  const auto merged = as_vector_u8(select(non_surrogate_mask, in, surrogates));
   24|       |
   25|  4.95k|  const auto shuffle = vector_u8::load(
   26|  4.95k|      (byte_order == endianness::LITTLE)
  ------------------
  |  Branch (26:7): [Folded, False: 4.95k]
  ------------------
   27|  4.95k|          ? tables::utf32_to_utf16::pack_utf32_to_utf16le[mask]
   28|  4.95k|          : tables::utf32_to_utf16::pack_utf32_to_utf16be[mask]);
   29|       |
   30|  4.95k|  const size_t u16count = (4 + count_ones(mask));
   31|  4.95k|  const auto compressed = shuffle.lookup_16(merged);
   32|       |
   33|  4.95k|  return {u16count, compressed};
   34|  4.95k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_138sse_convert_utf32_to_utf16_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPDsEEPKDimS7_:
  134|    748|                                       char16_t *utf16_output) {
  135|    748|  const char32_t *start = buf;
  136|    748|  const char32_t *end = buf + len;
  137|       |
  138|    748|  const __m128i v_ffff0000 = _mm_set1_epi32((int32_t)0xffff0000);
  139|       |
  140|  8.32k|  while (end - buf >= 8) {
  ------------------
  |  Branch (140:10): [True: 7.99k, False: 330]
  ------------------
  141|  7.99k|    const __m128i in = _mm_loadu_si128((__m128i *)buf);
  142|  7.99k|    const __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
  143|       |
  144|  7.99k|    const __m128i combined = _mm_or_si128(in, nextin);
  145|  7.99k|    if (simdutf_likely(_mm_testz_si128(combined, v_ffff0000))) {
  ------------------
  |  |   90|  7.99k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (90:31): [True: 5.92k, False: 2.07k]
  |  |  ------------------
  ------------------
  146|       |      // No bits set above 16th, directly pack UTF-32 to UTF-16
  147|  5.92k|      __m128i utf16_packed = _mm_packus_epi32(in, nextin);
  148|       |
  149|  5.92k|      const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800);
  150|  5.92k|      const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
  151|  5.92k|      const __m128i forbidden_bytemask =
  152|  5.92k|          _mm_cmpeq_epi16(_mm_and_si128(utf16_packed, v_f800), v_d800);
  153|  5.92k|      if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (153:11): [True: 34, False: 5.88k]
  ------------------
  154|     34|        return std::make_pair(result(error_code::SURROGATE, buf - start),
  155|     34|                              utf16_output);
  156|     34|      }
  157|       |
  158|  5.88k|      if (big_endian) {
  ------------------
  |  Branch (158:11): [Folded, False: 5.88k]
  ------------------
  159|      0|        const __m128i swap =
  160|      0|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  161|      0|        utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
  162|      0|      }
  163|       |
  164|  5.88k|      _mm_storeu_si128((__m128i *)utf16_output, utf16_packed);
  165|  5.88k|      utf16_output += 8;
  166|  5.88k|      buf += 8;
  167|  5.88k|    } else {
  168|  2.07k|      size_t forward = 7;
  169|  2.07k|      size_t k = 0;
  170|  2.07k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (170:11): [True: 0, False: 2.07k]
  ------------------
  171|      0|        forward = size_t(end - buf - 1);
  172|      0|      }
  173|  14.7k|      for (; k < forward; k++) {
  ------------------
  |  Branch (173:14): [True: 13.0k, False: 1.69k]
  ------------------
  174|  13.0k|        uint32_t word = buf[k];
  175|  13.0k|        if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (175:13): [True: 7.26k, False: 5.75k]
  ------------------
  176|       |          // will not generate a surrogate pair
  177|  7.26k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (177:15): [True: 900, False: 6.36k]
  |  Branch (177:33): [True: 60, False: 840]
  ------------------
  178|     60|            return std::make_pair(
  179|     60|                result(error_code::SURROGATE, buf - start + k), utf16_output);
  180|     60|          }
  181|  7.20k|          *utf16_output++ =
  182|  7.20k|              big_endian
  ------------------
  |  Branch (182:15): [Folded, False: 7.20k]
  ------------------
  183|  7.20k|                  ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8))
  184|  7.20k|                  : char16_t(word);
  185|  7.20k|        } else {
  186|       |          // will generate a surrogate pair
  187|  5.75k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (187:15): [True: 324, False: 5.42k]
  ------------------
  188|    324|            return std::make_pair(
  189|    324|                result(error_code::TOO_LARGE, buf - start + k), utf16_output);
  190|    324|          }
  191|  5.42k|          word -= 0x10000;
  192|  5.42k|          uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
  193|  5.42k|          uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
  194|  5.42k|          if (big_endian) {
  ------------------
  |  Branch (194:15): [Folded, False: 5.42k]
  ------------------
  195|      0|            high_surrogate =
  196|      0|                uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
  197|      0|            low_surrogate =
  198|      0|                uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
  199|      0|          }
  200|  5.42k|          *utf16_output++ = char16_t(high_surrogate);
  201|  5.42k|          *utf16_output++ = char16_t(low_surrogate);
  202|  5.42k|        }
  203|  13.0k|      }
  204|  1.69k|      buf += k;
  205|  1.69k|    }
  206|  7.99k|  }
  207|       |
  208|    330|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf16_output);
  209|    748|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_138sse_convert_utf32_to_utf16_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPDsEEPKDimS7_:
  134|    670|                                       char16_t *utf16_output) {
  135|    670|  const char32_t *start = buf;
  136|    670|  const char32_t *end = buf + len;
  137|       |
  138|    670|  const __m128i v_ffff0000 = _mm_set1_epi32((int32_t)0xffff0000);
  139|       |
  140|  5.87k|  while (end - buf >= 8) {
  ------------------
  |  Branch (140:10): [True: 5.56k, False: 312]
  ------------------
  141|  5.56k|    const __m128i in = _mm_loadu_si128((__m128i *)buf);
  142|  5.56k|    const __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
  143|       |
  144|  5.56k|    const __m128i combined = _mm_or_si128(in, nextin);
  145|  5.56k|    if (simdutf_likely(_mm_testz_si128(combined, v_ffff0000))) {
  ------------------
  |  |   90|  5.56k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (90:31): [True: 4.59k, False: 970]
  |  |  ------------------
  ------------------
  146|       |      // No bits set above 16th, directly pack UTF-32 to UTF-16
  147|  4.59k|      __m128i utf16_packed = _mm_packus_epi32(in, nextin);
  148|       |
  149|  4.59k|      const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800);
  150|  4.59k|      const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
  151|  4.59k|      const __m128i forbidden_bytemask =
  152|  4.59k|          _mm_cmpeq_epi16(_mm_and_si128(utf16_packed, v_f800), v_d800);
  153|  4.59k|      if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (153:11): [True: 18, False: 4.57k]
  ------------------
  154|     18|        return std::make_pair(result(error_code::SURROGATE, buf - start),
  155|     18|                              utf16_output);
  156|     18|      }
  157|       |
  158|  4.57k|      if (big_endian) {
  ------------------
  |  Branch (158:11): [True: 4.57k, Folded]
  ------------------
  159|  4.57k|        const __m128i swap =
  160|  4.57k|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  161|  4.57k|        utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
  162|  4.57k|      }
  163|       |
  164|  4.57k|      _mm_storeu_si128((__m128i *)utf16_output, utf16_packed);
  165|  4.57k|      utf16_output += 8;
  166|  4.57k|      buf += 8;
  167|  4.57k|    } else {
  168|    970|      size_t forward = 7;
  169|    970|      size_t k = 0;
  170|    970|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (170:11): [True: 0, False: 970]
  ------------------
  171|      0|        forward = size_t(end - buf - 1);
  172|      0|      }
  173|  6.10k|      for (; k < forward; k++) {
  ------------------
  |  Branch (173:14): [True: 5.47k, False: 630]
  ------------------
  174|  5.47k|        uint32_t word = buf[k];
  175|  5.47k|        if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (175:13): [True: 2.77k, False: 2.69k]
  ------------------
  176|       |          // will not generate a surrogate pair
  177|  2.77k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (177:15): [True: 660, False: 2.11k]
  |  Branch (177:33): [True: 34, False: 626]
  ------------------
  178|     34|            return std::make_pair(
  179|     34|                result(error_code::SURROGATE, buf - start + k), utf16_output);
  180|     34|          }
  181|  2.74k|          *utf16_output++ =
  182|  2.74k|              big_endian
  ------------------
  |  Branch (182:15): [True: 2.74k, Folded]
  ------------------
  183|  2.74k|                  ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8))
  184|  2.74k|                  : char16_t(word);
  185|  2.74k|        } else {
  186|       |          // will generate a surrogate pair
  187|  2.69k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (187:15): [True: 306, False: 2.39k]
  ------------------
  188|    306|            return std::make_pair(
  189|    306|                result(error_code::TOO_LARGE, buf - start + k), utf16_output);
  190|    306|          }
  191|  2.39k|          word -= 0x10000;
  192|  2.39k|          uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
  193|  2.39k|          uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
  194|  2.39k|          if (big_endian) {
  ------------------
  |  Branch (194:15): [True: 2.39k, Folded]
  ------------------
  195|  2.39k|            high_surrogate =
  196|  2.39k|                uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
  197|  2.39k|            low_surrogate =
  198|  2.39k|                uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
  199|  2.39k|          }
  200|  2.39k|          *utf16_output++ = char16_t(high_surrogate);
  201|  2.39k|          *utf16_output++ = char16_t(low_surrogate);
  202|  2.39k|        }
  203|  5.47k|      }
  204|    630|      buf += k;
  205|    630|    }
  206|  5.56k|  }
  207|       |
  208|    312|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf16_output);
  209|    670|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_125sse_convert_utf32_to_utf8EPKDimPc:
    2|    942|sse_convert_utf32_to_utf8(const char32_t *buf, size_t len, char *utf8_output) {
    3|    942|  const char32_t *end = buf + len;
    4|       |
    5|    942|  const __m128i v_0000 = _mm_setzero_si128();              //__m128 = 128 bits
    6|    942|  const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800); // 1111 1000 0000
    7|       |                                                           // 0000
    8|    942|  const __m128i v_c080 = _mm_set1_epi16((uint16_t)0xc080); // 1100 0000 1000
    9|       |                                                           // 0000
   10|    942|  const __m128i v_ff80 = _mm_set1_epi16((uint16_t)0xff80); // 1111 1111 1000
   11|       |                                                           // 0000
   12|    942|  const __m128i v_ffff0000 = _mm_set1_epi32(
   13|    942|      (uint32_t)0xffff0000); // 1111 1111 1111 1111 0000 0000 0000 0000
   14|    942|  const __m128i v_7fffffff = _mm_set1_epi32(
   15|    942|      (uint32_t)0x7fffffff); // 0111 1111 1111 1111 1111 1111 1111 1111
   16|    942|  __m128i running_max = _mm_setzero_si128();
   17|    942|  __m128i forbidden_bytemask = _mm_setzero_si128();
   18|    942|  const size_t safety_margin =
   19|    942|      12; // to avoid overruns, see issue
   20|       |          // https://github.com/simdutf/simdutf/issues/92
   21|       |
   22|  46.6k|  while (end - buf >=
  ------------------
  |  Branch (22:10): [True: 45.9k, False: 718]
  ------------------
   23|  46.6k|         std::ptrdiff_t(
   24|  46.6k|             16 + safety_margin)) { // buf is a char32_t pointer, each char32_t
   25|       |                                    // has 4 bytes or 32 bits, thus buf + 16 *
   26|       |                                    // char_32t = 512 bits = 64 bytes
   27|       |    // We load two 16 bytes registers for a total of 32 bytes or 16 characters.
   28|  45.9k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
   29|  45.9k|    __m128i nextin = _mm_loadu_si128(
   30|  45.9k|        (__m128i *)buf + 1); // These two values can hold only 8 UTF32 chars
   31|  45.9k|    running_max = _mm_max_epu32(
   32|  45.9k|        _mm_max_epu32(in, running_max), // take element-wise max char32_t from
   33|       |                                        // in and running_max vector
   34|  45.9k|        nextin); // and take element-wise max element from nextin and
   35|       |                 // running_max vector
   36|       |
   37|       |    // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned
   38|       |    // saturation
   39|  45.9k|    __m128i in_16 = _mm_packus_epi32(
   40|  45.9k|        _mm_and_si128(in, v_7fffffff),
   41|  45.9k|        _mm_and_si128(
   42|  45.9k|            nextin,
   43|  45.9k|            v_7fffffff)); // in this context pack the two __m128 into a single
   44|       |    // By ensuring the highest bit is set to 0(&v_7fffffff), we are making sure
   45|       |    // all values are interpreted as non-negative, or specifically, the values
   46|       |    // are within the range of valid Unicode code points. remember : having
   47|       |    // leading byte 0 means a positive number by the two complements system.
   48|       |    // Unicode is well beneath the range where you'll start getting issues so
   49|       |    // that's OK.
   50|       |
   51|       |    // Try to apply UTF-16 => UTF-8 from ./sse_convert_utf16_to_utf8.cpp
   52|       |
   53|       |    // Check for ASCII fast path
   54|       |
   55|       |    // ASCII fast path!!!!
   56|       |    // We eagerly load another 32 bytes, hoping that they will be ASCII too.
   57|       |    // The intuition is that we try to collect 16 ASCII characters which
   58|       |    // requires a total of 64 bytes of input. If we fail, we just pass thirdin
   59|       |    // and fourthin as our new inputs.
   60|  45.9k|    if (_mm_testz_si128(in_16, v_ff80)) { // if the first two blocks are ASCII
  ------------------
  |  Branch (60:9): [True: 35.9k, False: 10.0k]
  ------------------
   61|  35.9k|      __m128i thirdin = _mm_loadu_si128((__m128i *)buf + 2);
   62|  35.9k|      __m128i fourthin = _mm_loadu_si128((__m128i *)buf + 3);
   63|  35.9k|      running_max = _mm_max_epu32(
   64|  35.9k|          _mm_max_epu32(thirdin, running_max),
   65|  35.9k|          fourthin); // take the running max of all 4 vectors thus far
   66|  35.9k|      __m128i nextin_16 = _mm_packus_epi32(
   67|  35.9k|          _mm_and_si128(thirdin, v_7fffffff),
   68|  35.9k|          _mm_and_si128(fourthin,
   69|  35.9k|                        v_7fffffff)); // pack into 1 vector, now you have two
   70|  35.9k|      if (!_mm_testz_si128(
  ------------------
  |  Branch (70:11): [True: 1.28k, False: 34.6k]
  ------------------
   71|  35.9k|              nextin_16,
   72|  35.9k|              v_ff80)) { // checks if the second packed vector is ASCII, if not:
   73|       |        // 1. pack the bytes
   74|       |        // obviously suboptimal.
   75|  1.28k|        const __m128i utf8_packed = _mm_packus_epi16(
   76|  1.28k|            in_16, in_16); // creates two copy of in_16 in 1 vector
   77|       |        // 2. store (16 bytes)
   78|  1.28k|        _mm_storeu_si128((__m128i *)utf8_output,
   79|  1.28k|                         utf8_packed); // put them into the output
   80|       |        // 3. adjust pointers
   81|  1.28k|        buf += 8; // the char32_t buffer pointer goes up 8 char32_t chars* 32
   82|       |                  // bits =  256 bits
   83|  1.28k|        utf8_output +=
   84|  1.28k|            8; // same with output, e.g. lift the first two blocks alone.
   85|       |        // Proceed with next input
   86|  1.28k|        in_16 = nextin_16;
   87|       |        // We need to update in and nextin because they are used later.
   88|  1.28k|        in = thirdin;
   89|  1.28k|        nextin = fourthin;
   90|  34.6k|      } else {
   91|       |        // 1. pack the bytes
   92|  34.6k|        const __m128i utf8_packed = _mm_packus_epi16(in_16, nextin_16);
   93|       |        // 2. store (16 bytes)
   94|  34.6k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   95|       |        // 3. adjust pointers
   96|  34.6k|        buf += 16;
   97|  34.6k|        utf8_output += 16;
   98|  34.6k|        continue; // we are done for this round!
   99|  34.6k|      }
  100|  35.9k|    }
  101|       |
  102|       |    // no bits set above 7th bit -- find out all the ASCII characters
  103|  11.2k|    const __m128i one_byte_bytemask =
  104|  11.2k|        _mm_cmpeq_epi16( // this takes four bytes at a time and compares:
  105|  11.2k|            _mm_and_si128(in_16, v_ff80), // the vector that get only the first
  106|       |                                          // 9 bits of each 16-bit/2-byte units
  107|  11.2k|            v_0000                        //
  108|  11.2k|        ); // they should be all zero if they are ASCII. E.g. ASCII in UTF32 is
  109|       |           // of format 0000 0000 0000 0XXX XXXX
  110|       |    // _mm_cmpeq_epi16 should now return a 1111 1111 1111 1111 for equals, and
  111|       |    // 0000 0000 0000 0000 if not for each 16-bit/2-byte units
  112|  11.2k|    const uint16_t one_byte_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(
  113|  11.2k|        one_byte_bytemask)); // collect the MSB from previous vector and put
  114|       |                             // them into uint16_t mas
  115|       |
  116|       |    // no bits set above 11th bit
  117|  11.2k|    const __m128i one_or_two_bytes_bytemask =
  118|  11.2k|        _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_0000);
  119|  11.2k|    const uint16_t one_or_two_bytes_bitmask =
  120|  11.2k|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  121|       |
  122|  11.2k|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (122:9): [True: 7.69k, False: 3.59k]
  ------------------
  123|       |      // case: all code units either produce 1 or 2 UTF-8 bytes (at least one
  124|       |      // produces 2 bytes)
  125|       |      // 1. prepare 2-byte values
  126|       |      // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
  127|       |      // expected output   : [110a|aaaa|10bb|bbbb] x 8
  128|  7.69k|      const __m128i v_1f00 =
  129|  7.69k|          _mm_set1_epi16((int16_t)0x1f00); // 0001 1111 0000 0000
  130|  7.69k|      const __m128i v_003f =
  131|  7.69k|          _mm_set1_epi16((int16_t)0x003f); // 0000 0000 0011 1111
  132|       |
  133|       |      // t0 = [000a|aaaa|bbbb|bb00]
  134|  7.69k|      const __m128i t0 = _mm_slli_epi16(in_16, 2); // shift packed vector by two
  135|       |      // t1 = [000a|aaaa|0000|0000]
  136|  7.69k|      const __m128i t1 = _mm_and_si128(t0, v_1f00); // potential first utf8 byte
  137|       |      // t2 = [0000|0000|00bb|bbbb]
  138|  7.69k|      const __m128i t2 =
  139|  7.69k|          _mm_and_si128(in_16, v_003f); // potential second utf8 byte
  140|       |      // t3 = [000a|aaaa|00bb|bbbb]
  141|  7.69k|      const __m128i t3 =
  142|  7.69k|          _mm_or_si128(t1, t2); // first and second potential utf8 byte together
  143|       |      // t4 = [110a|aaaa|10bb|bbbb]
  144|  7.69k|      const __m128i t4 = _mm_or_si128(
  145|  7.69k|          t3,
  146|  7.69k|          v_c080); // t3 | 1100 0000 1000 0000 = full potential 2-byte utf8 unit
  147|       |
  148|       |      // 2. merge ASCII and 2-byte codewords
  149|  7.69k|      const __m128i utf8_unpacked =
  150|  7.69k|          _mm_blendv_epi8(t4, in_16, one_byte_bytemask);
  151|       |
  152|       |      // 3. prepare bitmask for 8-bit lookup
  153|       |      //    one_byte_bitmask = hhggffeeddccbbaa -- the bits are doubled (h -
  154|       |      //    MSB, a - LSB)
  155|  7.69k|      const uint16_t m0 = one_byte_bitmask & 0x5555; // m0 = 0h0g0f0e0d0c0b0a
  156|  7.69k|      const uint16_t m1 =
  157|  7.69k|          static_cast<uint16_t>(m0 >> 7); // m1 = 00000000h0g0f0e0
  158|  7.69k|      const uint8_t m2 =
  159|  7.69k|          static_cast<uint8_t>((m0 | m1) & 0xff); // m2 =         hdgcfbea
  160|       |      // 4. pack the bytes
  161|  7.69k|      const uint8_t *row =
  162|  7.69k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0];
  163|  7.69k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  164|  7.69k|      const __m128i utf8_packed = _mm_shuffle_epi8(utf8_unpacked, shuffle);
  165|       |
  166|       |      // 5. store bytes
  167|  7.69k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  168|       |
  169|       |      // 6. adjust pointers
  170|  7.69k|      buf += 8;
  171|  7.69k|      utf8_output += row[0];
  172|  7.69k|      continue;
  173|  7.69k|    }
  174|       |
  175|       |    // Check for overflow in packing
  176|       |
  177|  3.59k|    const __m128i saturation_bytemask = _mm_cmpeq_epi32(
  178|  3.59k|        _mm_and_si128(_mm_or_si128(in, nextin), v_ffff0000), v_0000);
  179|  3.59k|    const uint32_t saturation_bitmask =
  180|  3.59k|        static_cast<uint32_t>(_mm_movemask_epi8(saturation_bytemask));
  181|  3.59k|    if (saturation_bitmask == 0xffff) {
  ------------------
  |  Branch (181:9): [True: 1.84k, False: 1.75k]
  ------------------
  182|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  183|  1.84k|      const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
  184|  1.84k|      forbidden_bytemask =
  185|  1.84k|          _mm_or_si128(forbidden_bytemask,
  186|  1.84k|                       _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_d800));
  187|       |
  188|  1.84k|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  189|  1.84k|                                              0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
  190|       |
  191|       |      /* In this branch we handle three cases:
  192|       |          1. [0000|0000|0ccc|cccc] => [0ccc|cccc]                           -
  193|       |        single UFT-8 byte
  194|       |          2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc]              -
  195|       |        two UTF-8 bytes
  196|       |          3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] -
  197|       |        three UTF-8 bytes
  198|       |
  199|       |        We expand the input word (16-bit) into two code units (32-bit), thus
  200|       |        we have room for four bytes. However, we need five distinct bit
  201|       |        layouts. Note that the last byte in cases #2 and #3 is the same.
  202|       |
  203|       |        We precompute byte 1 for case #1 and the common byte for cases #2 & #3
  204|       |        in register t2.
  205|       |
  206|       |        We precompute byte 1 for case #3 and -- **conditionally** -- precompute
  207|       |        either byte 1 for case #2 or byte 2 for case #3. Note that they
  208|       |        differ by exactly one bit.
  209|       |
  210|       |        Finally from these two code units we build proper UTF-8 sequence, taking
  211|       |        into account the case (i.e, the number of bytes to write).
  212|       |      */
  213|       |      /**
  214|       |       * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
  215|       |       * t2 => [0ccc|cccc] [10cc|cccc]
  216|       |       * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
  217|       |       */
  218|  1.84k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  219|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  220|  1.84k|      const __m128i t0 = _mm_shuffle_epi8(in_16, dup_even);
  221|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  222|  1.84k|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  218|  1.84k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  223|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  224|  1.84k|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  218|  1.84k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  225|       |
  226|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  227|  1.84k|      const __m128i s0 = _mm_srli_epi16(in_16, 4);
  228|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  229|  1.84k|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  218|  1.84k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  230|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  231|  1.84k|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  218|  1.84k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  232|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  233|  1.84k|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  218|  1.84k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  234|  1.84k|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  235|  1.84k|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  218|  1.84k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  236|  1.84k|      const __m128i s4 = _mm_xor_si128(s3, m0);
  237|  1.84k|#undef simdutf_vec
  238|       |
  239|       |      // 4. expand code units 16-bit => 32-bit
  240|  1.84k|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  241|  1.84k|      const __m128i out1 = _mm_unpackhi_epi16(t2, s4);
  242|       |
  243|       |      // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
  244|  1.84k|      const uint16_t mask =
  245|  1.84k|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  246|  1.84k|      if (mask == 0) {
  ------------------
  |  Branch (246:11): [True: 810, False: 1.03k]
  ------------------
  247|       |        // We only have three-byte code units. Use fast path.
  248|    810|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  249|    810|                                              15, 13, -1, -1, -1, -1);
  250|    810|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  251|    810|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  252|    810|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  253|    810|        utf8_output += 12;
  254|    810|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  255|    810|        utf8_output += 12;
  256|    810|        buf += 8;
  257|    810|        continue;
  258|    810|      }
  259|  1.03k|      const uint8_t mask0 = uint8_t(mask);
  260|       |
  261|  1.03k|      const uint8_t *row0 =
  262|  1.03k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  263|  1.03k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  264|  1.03k|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  265|       |
  266|  1.03k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  267|       |
  268|  1.03k|      const uint8_t *row1 =
  269|  1.03k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  270|  1.03k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  271|  1.03k|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  272|       |
  273|  1.03k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  274|  1.03k|      utf8_output += row0[0];
  275|  1.03k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  276|  1.03k|      utf8_output += row1[0];
  277|       |
  278|  1.03k|      buf += 8;
  279|  1.75k|    } else {
  280|       |      // case: at least one 32-bit word produce a surrogate pair in UTF-16 <=>
  281|       |      // will produce four UTF-8 bytes Let us do a scalar fallback. It may seem
  282|       |      // wasteful to use scalar code, but being efficient with SIMD in the
  283|       |      // presence of surrogate pairs may require non-trivial tables.
  284|  1.75k|      size_t forward = 15;
  285|  1.75k|      size_t k = 0;
  286|  1.75k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (286:11): [True: 0, False: 1.75k]
  ------------------
  287|      0|        forward = size_t(end - buf - 1);
  288|      0|      }
  289|  25.5k|      for (; k < forward; k++) {
  ------------------
  |  Branch (289:14): [True: 24.0k, False: 1.53k]
  ------------------
  290|  24.0k|        uint32_t word = buf[k];
  291|  24.0k|        if ((word & 0xFFFFFF80) == 0) {
  ------------------
  |  Branch (291:13): [True: 10.7k, False: 13.2k]
  ------------------
  292|  10.7k|          *utf8_output++ = char(word);
  293|  13.2k|        } else if ((word & 0xFFFFF800) == 0) {
  ------------------
  |  Branch (293:20): [True: 2.81k, False: 10.4k]
  ------------------
  294|  2.81k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  295|  2.81k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  296|  10.4k|        } else if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (296:20): [True: 4.79k, False: 5.67k]
  ------------------
  297|  4.79k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (297:15): [True: 1.94k, False: 2.84k]
  |  Branch (297:33): [True: 24, False: 1.91k]
  ------------------
  298|     24|            return std::make_pair(nullptr, utf8_output);
  299|     24|          }
  300|  4.76k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  301|  4.76k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  302|  4.76k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  303|  5.67k|        } else {
  304|  5.67k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (304:15): [True: 200, False: 5.47k]
  ------------------
  305|    200|            return std::make_pair(nullptr, utf8_output);
  306|    200|          }
  307|  5.47k|          *utf8_output++ = char((word >> 18) | 0b11110000);
  308|  5.47k|          *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
  309|  5.47k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  310|  5.47k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  311|  5.47k|        }
  312|  24.0k|      }
  313|  1.53k|      buf += k;
  314|  1.53k|    }
  315|  3.59k|  } // while
  316|       |
  317|       |  // check for invalid input
  318|    718|  const __m128i v_10ffff = _mm_set1_epi32((uint32_t)0x10ffff);
  319|    718|  if (static_cast<uint16_t>(_mm_movemask_epi8(_mm_cmpeq_epi32(
  ------------------
  |  Branch (319:7): [True: 24, False: 694]
  ------------------
  320|    718|          _mm_max_epu32(running_max, v_10ffff), v_10ffff))) != 0xffff) {
  321|     24|    return std::make_pair(nullptr, utf8_output);
  322|     24|  }
  323|       |
  324|    694|  if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (324:7): [True: 46, False: 648]
  ------------------
  325|     46|    return std::make_pair(nullptr, utf8_output);
  326|     46|  }
  327|       |
  328|    648|  return std::make_pair(buf, utf8_output);
  329|    694|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_137sse_convert_utf32_to_utf8_with_errorsEPKDimPc:
  333|    864|                                      char *utf8_output) {
  334|    864|  const char32_t *end = buf + len;
  335|    864|  const char32_t *start = buf;
  336|       |
  337|    864|  const __m128i v_0000 = _mm_setzero_si128();
  338|    864|  const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800);
  339|    864|  const __m128i v_c080 = _mm_set1_epi16((uint16_t)0xc080);
  340|    864|  const __m128i v_ff80 = _mm_set1_epi16((uint16_t)0xff80);
  341|    864|  const __m128i v_ffff0000 = _mm_set1_epi32((uint32_t)0xffff0000);
  342|    864|  const __m128i v_7fffffff = _mm_set1_epi32((uint32_t)0x7fffffff);
  343|    864|  const __m128i v_10ffff = _mm_set1_epi32((uint32_t)0x10ffff);
  344|       |
  345|    864|  const size_t safety_margin =
  346|    864|      12; // to avoid overruns, see issue
  347|       |          // https://github.com/simdutf/simdutf/issues/92
  348|       |
  349|  24.7k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (349:10): [True: 24.0k, False: 662]
  ------------------
  350|       |    // We load two 16 bytes registers for a total of 32 bytes or 8 characters.
  351|  24.0k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
  352|  24.0k|    __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
  353|       |    // Check for too large input
  354|  24.0k|    __m128i max_input = _mm_max_epu32(_mm_max_epu32(in, nextin), v_10ffff);
  355|  24.0k|    if (static_cast<uint16_t>(_mm_movemask_epi8(
  ------------------
  |  Branch (355:9): [True: 68, False: 23.9k]
  ------------------
  356|  24.0k|            _mm_cmpeq_epi32(max_input, v_10ffff))) != 0xffff) {
  357|     68|      return std::make_pair(result(error_code::TOO_LARGE, buf - start),
  358|     68|                            utf8_output);
  359|     68|    }
  360|       |
  361|       |    // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned
  362|       |    // saturation
  363|  23.9k|    __m128i in_16 = _mm_packus_epi32(_mm_and_si128(in, v_7fffffff),
  364|  23.9k|                                     _mm_and_si128(nextin, v_7fffffff));
  365|       |
  366|       |    // Try to apply UTF-16 => UTF-8 from ./sse_convert_utf16_to_utf8.cpp
  367|       |
  368|       |    // Check for ASCII fast path
  369|  23.9k|    if (_mm_testz_si128(in_16, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (369:9): [True: 15.9k, False: 8.06k]
  ------------------
  370|       |      // 1. pack the bytes
  371|       |      // obviously suboptimal.
  372|  15.9k|      const __m128i utf8_packed = _mm_packus_epi16(in_16, in_16);
  373|       |      // 2. store (16 bytes)
  374|  15.9k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  375|       |      // 3. adjust pointers
  376|  15.9k|      buf += 8;
  377|  15.9k|      utf8_output += 8;
  378|  15.9k|      continue;
  379|  15.9k|    }
  380|       |
  381|       |    // no bits set above 7th bit
  382|  8.06k|    const __m128i one_byte_bytemask =
  383|  8.06k|        _mm_cmpeq_epi16(_mm_and_si128(in_16, v_ff80), v_0000);
  384|  8.06k|    const uint16_t one_byte_bitmask =
  385|  8.06k|        static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
  386|       |
  387|       |    // no bits set above 11th bit
  388|  8.06k|    const __m128i one_or_two_bytes_bytemask =
  389|  8.06k|        _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_0000);
  390|  8.06k|    const uint16_t one_or_two_bytes_bitmask =
  391|  8.06k|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  392|       |
  393|  8.06k|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (393:9): [True: 5.41k, False: 2.65k]
  ------------------
  394|       |      // case: all code units either produce 1 or 2 UTF-8 bytes (at least one
  395|       |      // produces 2 bytes)
  396|       |      // 1. prepare 2-byte values
  397|       |      // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
  398|       |      // expected output   : [110a|aaaa|10bb|bbbb] x 8
  399|  5.41k|      const __m128i v_1f00 = _mm_set1_epi16((int16_t)0x1f00);
  400|  5.41k|      const __m128i v_003f = _mm_set1_epi16((int16_t)0x003f);
  401|       |
  402|       |      // t0 = [000a|aaaa|bbbb|bb00]
  403|  5.41k|      const __m128i t0 = _mm_slli_epi16(in_16, 2);
  404|       |      // t1 = [000a|aaaa|0000|0000]
  405|  5.41k|      const __m128i t1 = _mm_and_si128(t0, v_1f00);
  406|       |      // t2 = [0000|0000|00bb|bbbb]
  407|  5.41k|      const __m128i t2 = _mm_and_si128(in_16, v_003f);
  408|       |      // t3 = [000a|aaaa|00bb|bbbb]
  409|  5.41k|      const __m128i t3 = _mm_or_si128(t1, t2);
  410|       |      // t4 = [110a|aaaa|10bb|bbbb]
  411|  5.41k|      const __m128i t4 = _mm_or_si128(t3, v_c080);
  412|       |
  413|       |      // 2. merge ASCII and 2-byte codewords
  414|  5.41k|      const __m128i utf8_unpacked =
  415|  5.41k|          _mm_blendv_epi8(t4, in_16, one_byte_bytemask);
  416|       |
  417|       |      // 3. prepare bitmask for 8-bit lookup
  418|       |      //    one_byte_bitmask = hhggffeeddccbbaa -- the bits are doubled (h -
  419|       |      //    MSB, a - LSB)
  420|  5.41k|      const uint16_t m0 = one_byte_bitmask & 0x5555; // m0 = 0h0g0f0e0d0c0b0a
  421|  5.41k|      const uint16_t m1 =
  422|  5.41k|          static_cast<uint16_t>(m0 >> 7); // m1 = 00000000h0g0f0e0
  423|  5.41k|      const uint8_t m2 =
  424|  5.41k|          static_cast<uint8_t>((m0 | m1) & 0xff); // m2 =         hdgcfbea
  425|       |      // 4. pack the bytes
  426|  5.41k|      const uint8_t *row =
  427|  5.41k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0];
  428|  5.41k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  429|  5.41k|      const __m128i utf8_packed = _mm_shuffle_epi8(utf8_unpacked, shuffle);
  430|       |
  431|       |      // 5. store bytes
  432|  5.41k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  433|       |
  434|       |      // 6. adjust pointers
  435|  5.41k|      buf += 8;
  436|  5.41k|      utf8_output += row[0];
  437|  5.41k|      continue;
  438|  5.41k|    }
  439|       |
  440|       |    // Check for overflow in packing
  441|  2.65k|    const __m128i saturation_bytemask = _mm_cmpeq_epi32(
  442|  2.65k|        _mm_and_si128(_mm_or_si128(in, nextin), v_ffff0000), v_0000);
  443|  2.65k|    const uint32_t saturation_bitmask =
  444|  2.65k|        static_cast<uint32_t>(_mm_movemask_epi8(saturation_bytemask));
  445|       |
  446|  2.65k|    if (saturation_bitmask == 0xffff) {
  ------------------
  |  Branch (446:9): [True: 1.28k, False: 1.36k]
  ------------------
  447|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  448|       |
  449|       |      // Check for illegal surrogate code units
  450|  1.28k|      const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
  451|  1.28k|      const __m128i forbidden_bytemask =
  452|  1.28k|          _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_d800);
  453|  1.28k|      if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (453:11): [True: 36, False: 1.25k]
  ------------------
  454|     36|        return std::make_pair(result(error_code::SURROGATE, buf - start),
  455|     36|                              utf8_output);
  456|     36|      }
  457|       |
  458|  1.25k|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  459|  1.25k|                                              0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
  460|       |
  461|       |      /* In this branch we handle three cases:
  462|       |          1. [0000|0000|0ccc|cccc] => [0ccc|cccc]                           -
  463|       |        single UFT-8 byte
  464|       |          2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc]              -
  465|       |        two UTF-8 bytes
  466|       |          3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] -
  467|       |        three UTF-8 bytes
  468|       |
  469|       |        We expand the input word (16-bit) into two code units (32-bit), thus
  470|       |        we have room for four bytes. However, we need five distinct bit
  471|       |        layouts. Note that the last byte in cases #2 and #3 is the same.
  472|       |
  473|       |        We precompute byte 1 for case #1 and the common byte for cases #2 & #3
  474|       |        in register t2.
  475|       |
  476|       |        We precompute byte 1 for case #3 and -- **conditionally** -- precompute
  477|       |        either byte 1 for case #2 or byte 2 for case #3. Note that they
  478|       |        differ by exactly one bit.
  479|       |
  480|       |        Finally from these two code units we build proper UTF-8 sequence, taking
  481|       |        into account the case (i.e, the number of bytes to write).
  482|       |      */
  483|       |      /**
  484|       |       * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
  485|       |       * t2 => [0ccc|cccc] [10cc|cccc]
  486|       |       * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
  487|       |       */
  488|  1.25k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  489|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  490|  1.25k|      const __m128i t0 = _mm_shuffle_epi8(in_16, dup_even);
  491|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  492|  1.25k|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  488|  1.25k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  493|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  494|  1.25k|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  488|  1.25k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  495|       |
  496|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  497|  1.25k|      const __m128i s0 = _mm_srli_epi16(in_16, 4);
  498|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  499|  1.25k|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  488|  1.25k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  500|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  501|  1.25k|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  488|  1.25k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  502|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  503|  1.25k|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  488|  1.25k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  504|  1.25k|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  505|  1.25k|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  488|  1.25k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  506|  1.25k|      const __m128i s4 = _mm_xor_si128(s3, m0);
  507|  1.25k|#undef simdutf_vec
  508|       |
  509|       |      // 4. expand code units 16-bit => 32-bit
  510|  1.25k|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  511|  1.25k|      const __m128i out1 = _mm_unpackhi_epi16(t2, s4);
  512|       |
  513|       |      // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
  514|  1.25k|      const uint16_t mask =
  515|  1.25k|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  516|  1.25k|      if (mask == 0) {
  ------------------
  |  Branch (516:11): [True: 442, False: 808]
  ------------------
  517|       |        // We only have three-byte code units. Use fast path.
  518|    442|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  519|    442|                                              15, 13, -1, -1, -1, -1);
  520|    442|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  521|    442|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  522|    442|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  523|    442|        utf8_output += 12;
  524|    442|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  525|    442|        utf8_output += 12;
  526|    442|        buf += 8;
  527|    442|        continue;
  528|    442|      }
  529|    808|      const uint8_t mask0 = uint8_t(mask);
  530|       |
  531|    808|      const uint8_t *row0 =
  532|    808|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  533|    808|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  534|    808|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  535|       |
  536|    808|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  537|       |
  538|    808|      const uint8_t *row1 =
  539|    808|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  540|    808|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  541|    808|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  542|       |
  543|    808|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  544|    808|      utf8_output += row0[0];
  545|    808|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  546|    808|      utf8_output += row1[0];
  547|       |
  548|    808|      buf += 8;
  549|  1.36k|    } else {
  550|       |      // case: at least one 32-bit word produce a surrogate pair in UTF-16 <=>
  551|       |      // will produce four UTF-8 bytes Let us do a scalar fallback. It may seem
  552|       |      // wasteful to use scalar code, but being efficient with SIMD in the
  553|       |      // presence of surrogate pairs may require non-trivial tables.
  554|  1.36k|      size_t forward = 15;
  555|  1.36k|      size_t k = 0;
  556|  1.36k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (556:11): [True: 0, False: 1.36k]
  ------------------
  557|      0|        forward = size_t(end - buf - 1);
  558|      0|      }
  559|  21.2k|      for (; k < forward; k++) {
  ------------------
  |  Branch (559:14): [True: 20.0k, False: 1.27k]
  ------------------
  560|  20.0k|        uint32_t word = buf[k];
  561|  20.0k|        if ((word & 0xFFFFFF80) == 0) {
  ------------------
  |  Branch (561:13): [True: 6.89k, False: 13.1k]
  ------------------
  562|  6.89k|          *utf8_output++ = char(word);
  563|  13.1k|        } else if ((word & 0xFFFFF800) == 0) {
  ------------------
  |  Branch (563:20): [True: 1.82k, False: 11.2k]
  ------------------
  564|  1.82k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  565|  1.82k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  566|  11.2k|        } else if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (566:20): [True: 3.46k, False: 7.82k]
  ------------------
  567|  3.46k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (567:15): [True: 1.32k, False: 2.14k]
  |  Branch (567:33): [True: 20, False: 1.30k]
  ------------------
  568|     20|            return std::make_pair(
  569|     20|                result(error_code::SURROGATE, buf - start + k), utf8_output);
  570|     20|          }
  571|  3.44k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  572|  3.44k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  573|  3.44k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  574|  7.82k|        } else {
  575|  7.82k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (575:15): [True: 78, False: 7.74k]
  ------------------
  576|     78|            return std::make_pair(
  577|     78|                result(error_code::TOO_LARGE, buf - start + k), utf8_output);
  578|     78|          }
  579|  7.74k|          *utf8_output++ = char((word >> 18) | 0b11110000);
  580|  7.74k|          *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
  581|  7.74k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  582|  7.74k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  583|  7.74k|        }
  584|  20.0k|      }
  585|  1.27k|      buf += k;
  586|  1.27k|    }
  587|  2.65k|  } // while
  588|    662|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  589|    864|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_129convert_masked_utf8_to_latin1EPKcmRPc:
    9|   302k|    char *&latin1_output) {
   10|       |  // we use an approach where we try to process up to 12 input bytes.
   11|       |  // Why 12 input bytes and not 16? Because we are concerned with the size of
   12|       |  // the lookup tables. Also 12 is nicely divisible by two and three.
   13|       |  //
   14|       |  //
   15|       |  // Optimization note: our main path below is load-latency dependent. Thus it
   16|       |  // is maybe beneficial to have fast paths that depend on branch prediction but
   17|       |  // have less latency. This results in more instructions but, potentially, also
   18|       |  // higher speeds.
   19|       |  //
   20|   302k|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   21|   302k|  const uint16_t input_utf8_end_of_code_point_mask =
   22|   302k|      utf8_end_of_code_point_mask &
   23|   302k|      0xfff; // we are only processing 12 bytes in case it is not all ASCII
   24|   302k|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (24:7): [True: 1.89k, False: 300k]
  ------------------
   25|       |    // We process the data in chunks of 12 bytes.
   26|  1.89k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(latin1_output), in);
   27|  1.89k|    latin1_output += 12; // We wrote 12 characters.
   28|  1.89k|    return 12;           // We consumed 12 bytes.
   29|  1.89k|  }
   30|       |  /// We do not have a fast path available, so we fallback.
   31|   300k|  const uint8_t idx =
   32|   300k|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
   33|   300k|  const uint8_t consumed =
   34|   300k|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
   35|       |  // this indicates an invalid input:
   36|   300k|  if (idx >= 64) {
  ------------------
  |  Branch (36:7): [True: 13.5k, False: 286k]
  ------------------
   37|  13.5k|    return consumed;
   38|  13.5k|  }
   39|       |  // Here we should have (idx < 64), if not, there is a bug in the validation or
   40|       |  // elsewhere. SIX (6) input code-code units this is a relatively easy scenario
   41|       |  // we process SIX (6) input code-code units. The max length in bytes of six
   42|       |  // code code units spanning between 1 and 2 bytes each is 12 bytes. On
   43|       |  // processors where pdep/pext is fast, we might be able to use a small lookup
   44|       |  // table.
   45|   286k|  const __m128i sh =
   46|   286k|      _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
   47|   286k|  const __m128i perm = _mm_shuffle_epi8(in, sh);
   48|   286k|  const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   49|   286k|  const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   50|   286k|  __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   51|   286k|  const __m128i latin1_packed = _mm_packus_epi16(composed, composed);
   52|       |  // writing 8 bytes even though we only care about the first 6 bytes.
   53|       |  // performance note: it would be faster to use _mm_storeu_si128, we should
   54|       |  // investigate.
   55|   286k|  _mm_storel_epi64((__m128i *)latin1_output, latin1_packed);
   56|   286k|  latin1_output += 6; // We wrote 6 bytes.
   57|   286k|  return consumed;
   58|   300k|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_128convert_masked_utf8_to_utf16ILNS_10endiannessE0EEEmPKcmRPDs:
   10|  2.79M|                                    char16_t *&utf16_output) {
   11|       |  // we use an approach where we try to process up to 12 input bytes.
   12|       |  // Why 12 input bytes and not 16? Because we are concerned with the size of
   13|       |  // the lookup tables. Also 12 is nicely divisible by two and three.
   14|       |  //
   15|       |  //
   16|       |  // Optimization note: our main path below is load-latency dependent. Thus it
   17|       |  // is maybe beneficial to have fast paths that depend on branch prediction but
   18|       |  // have less latency. This results in more instructions but, potentially, also
   19|       |  // higher speeds.
   20|       |  //
   21|       |  // We first try a few fast paths.
   22|  2.79M|  const __m128i swap =
   23|  2.79M|      _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   24|  2.79M|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   25|  2.79M|  const uint16_t input_utf8_end_of_code_point_mask =
   26|  2.79M|      utf8_end_of_code_point_mask & 0xfff;
   27|  2.79M|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (27:7): [True: 9.49k, False: 2.78M]
  ------------------
   28|       |    // We process the data in chunks of 12 bytes.
   29|       |    // Note: using 16 bytes is unsafe, see issue_ossfuzz_71218
   30|  9.49k|    __m128i ascii_first = _mm_cvtepu8_epi16(in);
   31|  9.49k|    __m128i ascii_second = _mm_cvtepu8_epi16(_mm_srli_si128(in, 8));
   32|  9.49k|    if (big_endian) {
  ------------------
  |  Branch (32:9): [Folded, False: 9.49k]
  ------------------
   33|      0|      ascii_first = _mm_shuffle_epi8(ascii_first, swap);
   34|      0|      ascii_second = _mm_shuffle_epi8(ascii_second, swap);
   35|      0|    }
   36|  9.49k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf16_output), ascii_first);
   37|  9.49k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf16_output + 8),
   38|  9.49k|                     ascii_second);
   39|  9.49k|    utf16_output += 12; // We wrote 12 16-bit characters.
   40|  9.49k|    return 12;          // We consumed 12 bytes.
   41|  9.49k|  }
   42|  2.78M|  if (((utf8_end_of_code_point_mask & 0xFFFF) == 0xaaaa)) {
  ------------------
  |  Branch (42:7): [True: 14.6k, False: 2.77M]
  ------------------
   43|       |    // We want to take 8 2-byte UTF-8 code units and turn them into 8 2-byte
   44|       |    // UTF-16 code units. There is probably a more efficient sequence, but the
   45|       |    // following might do.
   46|  14.6k|    const __m128i sh =
   47|  14.6k|        _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   48|  14.6k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   49|  14.6k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   50|  14.6k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   51|  14.6k|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   52|  14.6k|    if (big_endian)
  ------------------
  |  Branch (52:9): [Folded, False: 14.6k]
  ------------------
   53|      0|      composed = _mm_shuffle_epi8(composed, swap);
   54|  14.6k|    _mm_storeu_si128((__m128i *)utf16_output, composed);
   55|  14.6k|    utf16_output += 8; // We wrote 16 bytes, 8 code points.
   56|  14.6k|    return 16;
   57|  14.6k|  }
   58|  2.77M|  if (input_utf8_end_of_code_point_mask == 0x924) {
  ------------------
  |  Branch (58:7): [True: 4.14k, False: 2.76M]
  ------------------
   59|       |    // We want to take 4 3-byte UTF-8 code units and turn them into 4 2-byte
   60|       |    // UTF-16 code units. There is probably a more efficient sequence, but the
   61|       |    // following might do.
   62|  4.14k|    const __m128i sh =
   63|  4.14k|        _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
   64|  4.14k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   65|  4.14k|    const __m128i ascii =
   66|  4.14k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
   67|  4.14k|    const __m128i middlebyte =
   68|  4.14k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
   69|  4.14k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
   70|  4.14k|    const __m128i highbyte =
   71|  4.14k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
   72|  4.14k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
   73|  4.14k|    const __m128i composed =
   74|  4.14k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
   75|  4.14k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
   76|  4.14k|    if (big_endian)
  ------------------
  |  Branch (76:9): [Folded, False: 4.14k]
  ------------------
   77|      0|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
   78|  4.14k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
   79|  4.14k|    utf16_output += 4;
   80|  4.14k|    return 12;
   81|  4.14k|  }
   82|       |  /// We do not have a fast path available, so we fallback.
   83|       |
   84|  2.76M|  const uint8_t idx =
   85|  2.76M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
   86|  2.76M|  const uint8_t consumed =
   87|  2.76M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
   88|  2.76M|  if (idx < 64) {
  ------------------
  |  Branch (88:7): [True: 2.33M, False: 429k]
  ------------------
   89|       |    // SIX (6) input code-code units
   90|       |    // this is a relatively easy scenario
   91|       |    // we process SIX (6) input code-code units. The max length in bytes of six
   92|       |    // code code units spanning between 1 and 2 bytes each is 12 bytes. On
   93|       |    // processors where pdep/pext is fast, we might be able to use a small
   94|       |    // lookup table.
   95|  2.33M|    const __m128i sh =
   96|  2.33M|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
   97|  2.33M|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   98|  2.33M|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   99|  2.33M|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
  100|  2.33M|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
  101|  2.33M|    if (big_endian)
  ------------------
  |  Branch (101:9): [Folded, False: 2.33M]
  ------------------
  102|      0|      composed = _mm_shuffle_epi8(composed, swap);
  103|  2.33M|    _mm_storeu_si128((__m128i *)utf16_output, composed);
  104|  2.33M|    utf16_output += 6; // We wrote 12 bytes, 6 code points.
  105|  2.33M|  } else if (idx < 145) {
  ------------------
  |  Branch (105:14): [True: 374k, False: 55.1k]
  ------------------
  106|       |    // FOUR (4) input code-code units
  107|   374k|    const __m128i sh =
  108|   374k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
  109|   374k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  110|   374k|    const __m128i ascii =
  111|   374k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
  112|   374k|    const __m128i middlebyte =
  113|   374k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
  114|   374k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  115|   374k|    const __m128i highbyte =
  116|   374k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
  117|   374k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
  118|   374k|    const __m128i composed =
  119|   374k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
  120|   374k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
  121|   374k|    if (big_endian)
  ------------------
  |  Branch (121:9): [Folded, False: 374k]
  ------------------
  122|      0|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
  123|   374k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
  124|   374k|    utf16_output += 4;
  125|   374k|  } else if (idx < 209) {
  ------------------
  |  Branch (125:14): [True: 54.2k, False: 904]
  ------------------
  126|       |    // TWO (2) input code-code units
  127|       |    //////////////
  128|       |    // There might be garbage inputs where a leading byte mascarades as a
  129|       |    // four-byte leading byte (by being followed by 3 continuation byte), but is
  130|       |    // not greater than 0xf0. This could trigger a buffer overflow if we only
  131|       |    // counted leading bytes of the form 0xf0 as generating surrogate pairs,
  132|       |    // without further UTF-8 validation. Thus we must be careful to ensure that
  133|       |    // only leading bytes at least as large as 0xf0 generate surrogate pairs. We
  134|       |    // do as at the cost of an extra mask.
  135|       |    /////////////
  136|  54.2k|    const __m128i sh =
  137|  54.2k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
  138|  54.2k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  139|  54.2k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
  140|  54.2k|    const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
  141|  54.2k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  142|  54.2k|    __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
  143|       |    // correct for spurious high bit
  144|  54.2k|    const __m128i correct =
  145|  54.2k|        _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
  146|  54.2k|    middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
  147|  54.2k|    const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4);
  148|       |    // We deliberately carry the leading four bits in highbyte if they are
  149|       |    // present, we remove them later when computing hightenbits.
  150|  54.2k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0xff000000));
  151|  54.2k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6);
  152|       |    // When we need to generate a surrogate pair (leading byte > 0xF0), then
  153|       |    // the corresponding 32-bit value in 'composed'  will be greater than
  154|       |    // > (0xff00000>>6) or > 0x3c00000. This can be used later to identify the
  155|       |    // location of the surrogate pairs.
  156|  54.2k|    const __m128i composed =
  157|  54.2k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
  158|  54.2k|                     _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
  159|  54.2k|    const __m128i composedminus =
  160|  54.2k|        _mm_sub_epi32(composed, _mm_set1_epi32(0x10000));
  161|  54.2k|    const __m128i lowtenbits =
  162|  54.2k|        _mm_and_si128(composedminus, _mm_set1_epi32(0x3ff));
  163|       |    // Notice the 0x3ff mask:
  164|  54.2k|    const __m128i hightenbits =
  165|  54.2k|        _mm_and_si128(_mm_srli_epi32(composedminus, 10), _mm_set1_epi32(0x3ff));
  166|  54.2k|    const __m128i lowtenbitsadd =
  167|  54.2k|        _mm_add_epi32(lowtenbits, _mm_set1_epi32(0xDC00));
  168|  54.2k|    const __m128i hightenbitsadd =
  169|  54.2k|        _mm_add_epi32(hightenbits, _mm_set1_epi32(0xD800));
  170|  54.2k|    const __m128i lowtenbitsaddshifted = _mm_slli_epi32(lowtenbitsadd, 16);
  171|  54.2k|    __m128i surrogates = _mm_or_si128(hightenbitsadd, lowtenbitsaddshifted);
  172|  54.2k|    uint32_t basic_buffer[4];
  173|  54.2k|    uint32_t basic_buffer_swap[4];
  174|  54.2k|    if (big_endian) {
  ------------------
  |  Branch (174:9): [Folded, False: 54.2k]
  ------------------
  175|      0|      _mm_storeu_si128((__m128i *)basic_buffer_swap,
  176|      0|                       _mm_shuffle_epi8(composed, swap));
  177|      0|      surrogates = _mm_shuffle_epi8(surrogates, swap);
  178|      0|    }
  179|  54.2k|    _mm_storeu_si128((__m128i *)basic_buffer, composed);
  180|  54.2k|    uint32_t surrogate_buffer[4];
  181|  54.2k|    _mm_storeu_si128((__m128i *)surrogate_buffer, surrogates);
  182|   217k|    for (size_t i = 0; i < 3; i++) {
  ------------------
  |  Branch (182:24): [True: 162k, False: 54.2k]
  ------------------
  183|   162k|      if (basic_buffer[i] > 0x3c00000) {
  ------------------
  |  Branch (183:11): [True: 45.2k, False: 117k]
  ------------------
  184|  45.2k|        utf16_output[0] = uint16_t(surrogate_buffer[i] & 0xffff);
  185|  45.2k|        utf16_output[1] = uint16_t(surrogate_buffer[i] >> 16);
  186|  45.2k|        utf16_output += 2;
  187|   117k|      } else {
  188|   117k|        utf16_output[0] = big_endian ? uint16_t(basic_buffer_swap[i])
  ------------------
  |  Branch (188:27): [Folded, False: 117k]
  ------------------
  189|   117k|                                     : uint16_t(basic_buffer[i]);
  190|   117k|        utf16_output++;
  191|   117k|      }
  192|   162k|    }
  193|  54.2k|  } else {
  194|       |    // here we know that there is an error but we do not handle errors
  195|    904|  }
  196|  2.76M|  return consumed;
  197|  2.77M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_128convert_masked_utf8_to_utf16ILNS_10endiannessE1EEEmPKcmRPDs:
   10|  4.08M|                                    char16_t *&utf16_output) {
   11|       |  // we use an approach where we try to process up to 12 input bytes.
   12|       |  // Why 12 input bytes and not 16? Because we are concerned with the size of
   13|       |  // the lookup tables. Also 12 is nicely divisible by two and three.
   14|       |  //
   15|       |  //
   16|       |  // Optimization note: our main path below is load-latency dependent. Thus it
   17|       |  // is maybe beneficial to have fast paths that depend on branch prediction but
   18|       |  // have less latency. This results in more instructions but, potentially, also
   19|       |  // higher speeds.
   20|       |  //
   21|       |  // We first try a few fast paths.
   22|  4.08M|  const __m128i swap =
   23|  4.08M|      _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   24|  4.08M|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   25|  4.08M|  const uint16_t input_utf8_end_of_code_point_mask =
   26|  4.08M|      utf8_end_of_code_point_mask & 0xfff;
   27|  4.08M|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (27:7): [True: 14.3k, False: 4.06M]
  ------------------
   28|       |    // We process the data in chunks of 12 bytes.
   29|       |    // Note: using 16 bytes is unsafe, see issue_ossfuzz_71218
   30|  14.3k|    __m128i ascii_first = _mm_cvtepu8_epi16(in);
   31|  14.3k|    __m128i ascii_second = _mm_cvtepu8_epi16(_mm_srli_si128(in, 8));
   32|  14.3k|    if (big_endian) {
  ------------------
  |  Branch (32:9): [True: 14.3k, Folded]
  ------------------
   33|  14.3k|      ascii_first = _mm_shuffle_epi8(ascii_first, swap);
   34|  14.3k|      ascii_second = _mm_shuffle_epi8(ascii_second, swap);
   35|  14.3k|    }
   36|  14.3k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf16_output), ascii_first);
   37|  14.3k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf16_output + 8),
   38|  14.3k|                     ascii_second);
   39|  14.3k|    utf16_output += 12; // We wrote 12 16-bit characters.
   40|  14.3k|    return 12;          // We consumed 12 bytes.
   41|  14.3k|  }
   42|  4.06M|  if (((utf8_end_of_code_point_mask & 0xFFFF) == 0xaaaa)) {
  ------------------
  |  Branch (42:7): [True: 29.8k, False: 4.03M]
  ------------------
   43|       |    // We want to take 8 2-byte UTF-8 code units and turn them into 8 2-byte
   44|       |    // UTF-16 code units. There is probably a more efficient sequence, but the
   45|       |    // following might do.
   46|  29.8k|    const __m128i sh =
   47|  29.8k|        _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   48|  29.8k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   49|  29.8k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   50|  29.8k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   51|  29.8k|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   52|  29.8k|    if (big_endian)
  ------------------
  |  Branch (52:9): [True: 29.8k, Folded]
  ------------------
   53|  29.8k|      composed = _mm_shuffle_epi8(composed, swap);
   54|  29.8k|    _mm_storeu_si128((__m128i *)utf16_output, composed);
   55|  29.8k|    utf16_output += 8; // We wrote 16 bytes, 8 code points.
   56|  29.8k|    return 16;
   57|  29.8k|  }
   58|  4.03M|  if (input_utf8_end_of_code_point_mask == 0x924) {
  ------------------
  |  Branch (58:7): [True: 13.9k, False: 4.02M]
  ------------------
   59|       |    // We want to take 4 3-byte UTF-8 code units and turn them into 4 2-byte
   60|       |    // UTF-16 code units. There is probably a more efficient sequence, but the
   61|       |    // following might do.
   62|  13.9k|    const __m128i sh =
   63|  13.9k|        _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
   64|  13.9k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   65|  13.9k|    const __m128i ascii =
   66|  13.9k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
   67|  13.9k|    const __m128i middlebyte =
   68|  13.9k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
   69|  13.9k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
   70|  13.9k|    const __m128i highbyte =
   71|  13.9k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
   72|  13.9k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
   73|  13.9k|    const __m128i composed =
   74|  13.9k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
   75|  13.9k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
   76|  13.9k|    if (big_endian)
  ------------------
  |  Branch (76:9): [True: 13.9k, Folded]
  ------------------
   77|  13.9k|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
   78|  13.9k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
   79|  13.9k|    utf16_output += 4;
   80|  13.9k|    return 12;
   81|  13.9k|  }
   82|       |  /// We do not have a fast path available, so we fallback.
   83|       |
   84|  4.02M|  const uint8_t idx =
   85|  4.02M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
   86|  4.02M|  const uint8_t consumed =
   87|  4.02M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
   88|  4.02M|  if (idx < 64) {
  ------------------
  |  Branch (88:7): [True: 3.62M, False: 402k]
  ------------------
   89|       |    // SIX (6) input code-code units
   90|       |    // this is a relatively easy scenario
   91|       |    // we process SIX (6) input code-code units. The max length in bytes of six
   92|       |    // code code units spanning between 1 and 2 bytes each is 12 bytes. On
   93|       |    // processors where pdep/pext is fast, we might be able to use a small
   94|       |    // lookup table.
   95|  3.62M|    const __m128i sh =
   96|  3.62M|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
   97|  3.62M|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   98|  3.62M|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   99|  3.62M|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
  100|  3.62M|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
  101|  3.62M|    if (big_endian)
  ------------------
  |  Branch (101:9): [True: 3.62M, Folded]
  ------------------
  102|  3.62M|      composed = _mm_shuffle_epi8(composed, swap);
  103|  3.62M|    _mm_storeu_si128((__m128i *)utf16_output, composed);
  104|  3.62M|    utf16_output += 6; // We wrote 12 bytes, 6 code points.
  105|  3.62M|  } else if (idx < 145) {
  ------------------
  |  Branch (105:14): [True: 297k, False: 104k]
  ------------------
  106|       |    // FOUR (4) input code-code units
  107|   297k|    const __m128i sh =
  108|   297k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
  109|   297k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  110|   297k|    const __m128i ascii =
  111|   297k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
  112|   297k|    const __m128i middlebyte =
  113|   297k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
  114|   297k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  115|   297k|    const __m128i highbyte =
  116|   297k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
  117|   297k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
  118|   297k|    const __m128i composed =
  119|   297k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
  120|   297k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
  121|   297k|    if (big_endian)
  ------------------
  |  Branch (121:9): [True: 297k, Folded]
  ------------------
  122|   297k|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
  123|   297k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
  124|   297k|    utf16_output += 4;
  125|   297k|  } else if (idx < 209) {
  ------------------
  |  Branch (125:14): [True: 103k, False: 1.67k]
  ------------------
  126|       |    // TWO (2) input code-code units
  127|       |    //////////////
  128|       |    // There might be garbage inputs where a leading byte mascarades as a
  129|       |    // four-byte leading byte (by being followed by 3 continuation byte), but is
  130|       |    // not greater than 0xf0. This could trigger a buffer overflow if we only
  131|       |    // counted leading bytes of the form 0xf0 as generating surrogate pairs,
  132|       |    // without further UTF-8 validation. Thus we must be careful to ensure that
  133|       |    // only leading bytes at least as large as 0xf0 generate surrogate pairs. We
  134|       |    // do as at the cost of an extra mask.
  135|       |    /////////////
  136|   103k|    const __m128i sh =
  137|   103k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
  138|   103k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  139|   103k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
  140|   103k|    const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
  141|   103k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  142|   103k|    __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
  143|       |    // correct for spurious high bit
  144|   103k|    const __m128i correct =
  145|   103k|        _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
  146|   103k|    middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
  147|   103k|    const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4);
  148|       |    // We deliberately carry the leading four bits in highbyte if they are
  149|       |    // present, we remove them later when computing hightenbits.
  150|   103k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0xff000000));
  151|   103k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6);
  152|       |    // When we need to generate a surrogate pair (leading byte > 0xF0), then
  153|       |    // the corresponding 32-bit value in 'composed'  will be greater than
  154|       |    // > (0xff00000>>6) or > 0x3c00000. This can be used later to identify the
  155|       |    // location of the surrogate pairs.
  156|   103k|    const __m128i composed =
  157|   103k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
  158|   103k|                     _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
  159|   103k|    const __m128i composedminus =
  160|   103k|        _mm_sub_epi32(composed, _mm_set1_epi32(0x10000));
  161|   103k|    const __m128i lowtenbits =
  162|   103k|        _mm_and_si128(composedminus, _mm_set1_epi32(0x3ff));
  163|       |    // Notice the 0x3ff mask:
  164|   103k|    const __m128i hightenbits =
  165|   103k|        _mm_and_si128(_mm_srli_epi32(composedminus, 10), _mm_set1_epi32(0x3ff));
  166|   103k|    const __m128i lowtenbitsadd =
  167|   103k|        _mm_add_epi32(lowtenbits, _mm_set1_epi32(0xDC00));
  168|   103k|    const __m128i hightenbitsadd =
  169|   103k|        _mm_add_epi32(hightenbits, _mm_set1_epi32(0xD800));
  170|   103k|    const __m128i lowtenbitsaddshifted = _mm_slli_epi32(lowtenbitsadd, 16);
  171|   103k|    __m128i surrogates = _mm_or_si128(hightenbitsadd, lowtenbitsaddshifted);
  172|   103k|    uint32_t basic_buffer[4];
  173|   103k|    uint32_t basic_buffer_swap[4];
  174|   103k|    if (big_endian) {
  ------------------
  |  Branch (174:9): [True: 103k, Folded]
  ------------------
  175|   103k|      _mm_storeu_si128((__m128i *)basic_buffer_swap,
  176|   103k|                       _mm_shuffle_epi8(composed, swap));
  177|   103k|      surrogates = _mm_shuffle_epi8(surrogates, swap);
  178|   103k|    }
  179|   103k|    _mm_storeu_si128((__m128i *)basic_buffer, composed);
  180|   103k|    uint32_t surrogate_buffer[4];
  181|   103k|    _mm_storeu_si128((__m128i *)surrogate_buffer, surrogates);
  182|   412k|    for (size_t i = 0; i < 3; i++) {
  ------------------
  |  Branch (182:24): [True: 309k, False: 103k]
  ------------------
  183|   309k|      if (basic_buffer[i] > 0x3c00000) {
  ------------------
  |  Branch (183:11): [True: 86.2k, False: 222k]
  ------------------
  184|  86.2k|        utf16_output[0] = uint16_t(surrogate_buffer[i] & 0xffff);
  185|  86.2k|        utf16_output[1] = uint16_t(surrogate_buffer[i] >> 16);
  186|  86.2k|        utf16_output += 2;
  187|   222k|      } else {
  188|   222k|        utf16_output[0] = big_endian ? uint16_t(basic_buffer_swap[i])
  ------------------
  |  Branch (188:27): [True: 222k, Folded]
  ------------------
  189|   222k|                                     : uint16_t(basic_buffer[i]);
  190|   222k|        utf16_output++;
  191|   222k|      }
  192|   309k|    }
  193|   103k|  } else {
  194|       |    // here we know that there is an error but we do not handle errors
  195|  1.67k|  }
  196|  4.02M|  return consumed;
  197|  4.03M|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_128convert_masked_utf8_to_utf32EPKcmRPDi:
    9|  3.88M|                                    char32_t *&utf32_output) {
   10|       |  // we use an approach where we try to process up to 12 input bytes.
   11|       |  // Why 12 input bytes and not 16? Because we are concerned with the size of
   12|       |  // the lookup tables. Also 12 is nicely divisible by two and three.
   13|       |  //
   14|       |  //
   15|       |  // Optimization note: our main path below is load-latency dependent. Thus it
   16|       |  // is maybe beneficial to have fast paths that depend on branch prediction but
   17|       |  // have less latency. This results in more instructions but, potentially, also
   18|       |  // higher speeds.
   19|       |  //
   20|       |  // We first try a few fast paths.
   21|  3.88M|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   22|  3.88M|  const uint16_t input_utf8_end_of_code_point_mask =
   23|  3.88M|      utf8_end_of_code_point_mask & 0xfff;
   24|  3.88M|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (24:7): [True: 10.2k, False: 3.87M]
  ------------------
   25|       |    // We process the data in chunks of 12 bytes.
   26|  10.2k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output),
   27|  10.2k|                     _mm_cvtepu8_epi32(in));
   28|  10.2k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 4),
   29|  10.2k|                     _mm_cvtepu8_epi32(_mm_srli_si128(in, 4)));
   30|  10.2k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 8),
   31|  10.2k|                     _mm_cvtepu8_epi32(_mm_srli_si128(in, 8)));
   32|  10.2k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 12),
   33|  10.2k|                     _mm_cvtepu8_epi32(_mm_srli_si128(in, 12)));
   34|  10.2k|    utf32_output += 12; // We wrote 12 32-bit characters.
   35|  10.2k|    return 12;          // We consumed 12 bytes.
   36|  10.2k|  }
   37|  3.87M|  if (((utf8_end_of_code_point_mask & 0xffff) == 0xaaaa)) {
  ------------------
  |  Branch (37:7): [True: 23.8k, False: 3.85M]
  ------------------
   38|       |    // We want to take 8 2-byte UTF-8 code units and turn them into 8 4-byte
   39|       |    // UTF-32 code units. There is probably a more efficient sequence, but the
   40|       |    // following might do.
   41|  23.8k|    const __m128i sh =
   42|  23.8k|        _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   43|  23.8k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   44|  23.8k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   45|  23.8k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   46|  23.8k|    const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   47|  23.8k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output),
   48|  23.8k|                     _mm_cvtepu16_epi32(composed));
   49|  23.8k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 4),
   50|  23.8k|                     _mm_cvtepu16_epi32(_mm_srli_si128(composed, 8)));
   51|  23.8k|    utf32_output += 8; // We wrote 32 bytes, 8 code points.
   52|  23.8k|    return 16;
   53|  23.8k|  }
   54|  3.85M|  if (input_utf8_end_of_code_point_mask == 0x924) {
  ------------------
  |  Branch (54:7): [True: 57.8k, False: 3.79M]
  ------------------
   55|       |    // We want to take 4 3-byte UTF-8 code units and turn them into 4 4-byte
   56|       |    // UTF-32 code units. There is probably a more efficient sequence, but the
   57|       |    // following might do.
   58|  57.8k|    const __m128i sh =
   59|  57.8k|        _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
   60|  57.8k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   61|  57.8k|    const __m128i ascii =
   62|  57.8k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
   63|  57.8k|    const __m128i middlebyte =
   64|  57.8k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
   65|  57.8k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
   66|  57.8k|    const __m128i highbyte =
   67|  57.8k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
   68|  57.8k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
   69|  57.8k|    const __m128i composed =
   70|  57.8k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
   71|  57.8k|    _mm_storeu_si128((__m128i *)utf32_output, composed);
   72|  57.8k|    utf32_output += 4;
   73|  57.8k|    return 12;
   74|  57.8k|  }
   75|       |  /// We do not have a fast path available, so we fallback.
   76|       |
   77|  3.79M|  const uint8_t idx =
   78|  3.79M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
   79|  3.79M|  const uint8_t consumed =
   80|  3.79M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
   81|  3.79M|  if (idx < 64) {
  ------------------
  |  Branch (81:7): [True: 3.47M, False: 326k]
  ------------------
   82|       |    // SIX (6) input code-code units
   83|       |    // this is a relatively easy scenario
   84|       |    // we process SIX (6) input code-code units. The max length in bytes of six
   85|       |    // code code units spanning between 1 and 2 bytes each is 12 bytes. On
   86|       |    // processors where pdep/pext is fast, we might be able to use a small
   87|       |    // lookup table.
   88|  3.47M|    const __m128i sh =
   89|  3.47M|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
   90|  3.47M|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   91|  3.47M|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   92|  3.47M|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   93|  3.47M|    const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   94|  3.47M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output),
   95|  3.47M|                     _mm_cvtepu16_epi32(composed));
   96|  3.47M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 4),
   97|  3.47M|                     _mm_cvtepu16_epi32(_mm_srli_si128(composed, 8)));
   98|  3.47M|    utf32_output += 6; // We wrote 12 bytes, 6 code points.
   99|  3.47M|  } else if (idx < 145) {
  ------------------
  |  Branch (99:14): [True: 263k, False: 63.0k]
  ------------------
  100|       |    // FOUR (4) input code-code units
  101|   263k|    const __m128i sh =
  102|   263k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
  103|   263k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  104|   263k|    const __m128i ascii =
  105|   263k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
  106|   263k|    const __m128i middlebyte =
  107|   263k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
  108|   263k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  109|   263k|    const __m128i highbyte =
  110|   263k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
  111|   263k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
  112|   263k|    const __m128i composed =
  113|   263k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
  114|   263k|    _mm_storeu_si128((__m128i *)utf32_output, composed);
  115|   263k|    utf32_output += 4;
  116|   263k|  } else if (idx < 209) {
  ------------------
  |  Branch (116:14): [True: 61.8k, False: 1.23k]
  ------------------
  117|       |    // TWO (2) input code-code units
  118|  61.8k|    const __m128i sh =
  119|  61.8k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
  120|  61.8k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  121|  61.8k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
  122|  61.8k|    const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
  123|  61.8k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  124|  61.8k|    __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
  125|       |    // correct for spurious high bit
  126|  61.8k|    const __m128i correct =
  127|  61.8k|        _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
  128|  61.8k|    middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
  129|  61.8k|    const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4);
  130|  61.8k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0x07000000));
  131|  61.8k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6);
  132|  61.8k|    const __m128i composed =
  133|  61.8k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
  134|  61.8k|                     _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
  135|  61.8k|    _mm_storeu_si128((__m128i *)utf32_output, composed);
  136|  61.8k|    utf32_output += 3;
  137|  61.8k|  } else {
  138|       |    // here we know that there is an error but we do not handle errors
  139|  1.23k|  }
  140|  3.79M|  return consumed;
  141|  3.85M|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_123utf16_gather_high_bytesILNS_10endiannessE0EEENS1_4simd5simd8IhEENS4_6simd16ItEES8_:
    3|  2.71M|                                       const simd16<uint16_t> in1) {
    4|  2.71M|  if (big_endian) {
  ------------------
  |  Branch (4:7): [Folded, False: 2.71M]
  ------------------
    5|       |    // we want lower bytes
    6|      0|    const auto mask = simd16<uint16_t>(0x00ff);
    7|      0|    const auto t0 = in0 & mask;
    8|      0|    const auto t1 = in1 & mask;
    9|       |
   10|      0|    return simd16<uint16_t>::pack(t0, t1);
   11|  2.71M|  } else {
   12|  2.71M|    const auto t0 = in0.shr<8>();
   13|  2.71M|    const auto t1 = in1.shr<8>();
   14|       |
   15|  2.71M|    return simd16<uint16_t>::pack(t0, t1);
   16|  2.71M|  }
   17|  2.71M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_123utf16_gather_high_bytesILNS_10endiannessE1EEENS1_4simd5simd8IhEENS4_6simd16ItEES8_:
    3|  2.63M|                                       const simd16<uint16_t> in1) {
    4|  2.63M|  if (big_endian) {
  ------------------
  |  Branch (4:7): [True: 2.63M, Folded]
  ------------------
    5|       |    // we want lower bytes
    6|  2.63M|    const auto mask = simd16<uint16_t>(0x00ff);
    7|  2.63M|    const auto t0 = in0 & mask;
    8|  2.63M|    const auto t1 = in1 & mask;
    9|       |
   10|  2.63M|    return simd16<uint16_t>::pack(t0, t1);
   11|  2.63M|  } else {
   12|      0|    const auto t0 = in0.shr<8>();
   13|      0|    const auto t1 = in1.shr<8>();
   14|       |
   15|      0|    return simd16<uint16_t>::pack(t0, t1);
   16|      0|  }
   17|  2.63M|}

