_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|  9.08k|extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  640|  9.08k|  static const auto fptrs = populate_functions();
  641|  9.08k|  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|  9.08k|  if (size < 4) {
  ------------------
  |  Branch (646:7): [True: 3, False: 9.08k]
  ------------------
  647|      3|    return 0;
  648|      3|  }
  649|       |
  650|  9.08k|  constexpr auto actionmask = std::bit_ceil(Ncases) - 1;
  651|  9.08k|  const auto action = data[0] & actionmask;
  652|  9.08k|  data += 4;
  653|  9.08k|  size -= 4;
  654|       |
  655|  9.08k|  if (action >= Ncases) {
  ------------------
  |  Branch (655:7): [True: 1, False: 9.07k]
  ------------------
  656|      1|    return 0;
  657|      1|  }
  658|       |
  659|  9.07k|  if constexpr (use_separate_allocation) {
  660|       |    // this is better at exercising null input and catch buffer underflows
  661|  9.07k|    const std::vector<char> separate{data, data + size};
  662|  9.07k|    fptrs[action](std::span(separate));
  663|       |  } else {
  664|       |    std::span<const char> chardata{(const char*)data, size};
  665|       |    fptrs[action](chardata);
  666|       |  }
  667|       |
  668|  9.07k|  return 0;
  669|  9.08k|}
conversion.cpp:_ZZ18populate_functionsvENK3$_0clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     89|    +[](std::span<const char> chardata) {                                      \
  555|     89|      const auto c =                                                           \
  556|     89|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     89|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     89|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     89|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     89|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     89|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     89|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     89|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     89|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     89|  constexpr auto _size = _name.size();                                \
  |  | 1183|     89|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     89|  return _nameof; }()
  ------------------
  561|     89|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     89|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     89|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     89|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     89|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     89|  constexpr auto _size = _name.size();                                \
  |  | 1183|     89|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     89|  return _nameof; }()
  ------------------
  562|     89|      c.fuzz(chardata);                                                        \
  563|     89|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    208|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    208|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    208|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    208|    static const bool do_print_testcase =
  181|    208|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    208|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 208]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    208|    do {
  189|       |      // step 0 - is the input valid?
  190|    208|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    208|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 208]
  |  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|    208|                    From == UtfEncodings::UTF8) {
  198|    208|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 208]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    208|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    208|      const auto [output_length, length_agree] =
  204|    208|          calculate_length(from, inputisvalid);
  205|    208|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 208]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    208|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 91, False: 117]
  |  Branch (208:28): [True: 16, False: 75]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|     16|        return;
  211|     16|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    192|      const auto [written, outputs_agree] =
  215|    192|          do_conversion(from, output_length, inputisvalid);
  216|    192|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 192]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    192|      return;
  221|    192|    } 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|    208|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    208|  validation_result verify_valid_input(FromSpan src) const {
  231|    208|    validation_result ret{};
  232|       |
  233|    208|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    208|    const auto implementations = get_supported_implementations();
  235|    208|    std::vector<simdutf::result> results;
  236|    208|    results.reserve(implementations.size());
  237|       |
  238|    624|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 624, False: 208]
  ------------------
  239|    624|      results.push_back(
  240|    624|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    624|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    624|      const bool validation2 =
  245|    624|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    624|                      src.data(), src.size());
  247|    624|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 624]
  ------------------
  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|    624|    }
  258|       |
  259|    208|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    208|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 208]
  ------------------
  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|    208|    } else {
  273|    208|      ret.implementations_agree = true;
  274|    208|    }
  275|    208|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    208|      return r.error == simdutf::SUCCESS;
  277|    208|    });
  278|    208|    return ret;
  279|    208|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    416|    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|    442|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    442|      return r.error == simdutf::SUCCESS;
  277|    442|    });
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    208|  bool count_the_input(FromSpan src) const {
  290|    208|    const auto implementations = get_supported_implementations();
  291|    208|    std::vector<std::size_t> results;
  292|    208|    results.reserve(implementations.size());
  293|       |
  294|    624|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 624, False: 208]
  ------------------
  295|    624|      std::size_t ret;
  296|    624|      if constexpr (From == UtfEncodings::UTF16BE) {
  297|    624|        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|    624|      results.push_back(ret);
  304|    624|    }
  305|    208|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    208|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 208]
  ------------------
  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|    208|    return true;
  321|    208|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|    416|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    208|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    208|    length_result ret{};
  344|       |
  345|    208|    const auto implementations = get_supported_implementations();
  346|    208|    std::vector<std::size_t> results;
  347|    208|    results.reserve(implementations.size());
  348|       |
  349|    624|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 624, False: 208]
  ------------------
  350|    624|      const auto len = invoke_lengthcalc(impl, src);
  351|    624|      results.push_back(len);
  352|    624|      ret.length.push_back(len);
  353|    624|    }
  354|       |
  355|    208|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    208|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 208]
  ------------------
  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|    208|    } else {
  375|    208|      ret.implementations_agree = true;
  376|    208|    }
  377|    208|    return ret;
  378|    208|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    624|                                FromSpan src) const {
  331|    624|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    624|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    416|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    192|                                  const bool inputisvalid) const {
  383|    192|    conversion_result ret{};
  384|       |
  385|    192|    const auto implementations = get_supported_implementations();
  386|       |
  387|    192|    std::vector<result<ConversionResult>> results;
  388|    192|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    192|    std::vector<std::vector<ToType>> outputbuffers;
  393|    192|    outputbuffers.reserve(implementations.size());
  394|    768|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 576, False: 192]
  ------------------
  395|    576|      auto impl = implementations[i];
  396|    576|      const ToType canary1{42};
  397|    576|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    576|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    576|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    576|      const auto success = [](const ConversionResult& r) -> bool {
  402|    576|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    576|          return r != 0;
  404|    576|        } else {
  405|    576|          return r.error == simdutf::error_code::SUCCESS;
  406|    576|        }
  407|    576|      }(implret1);
  408|    576|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    576|      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|    576|        const ToType canary2{25};
  414|    576|        const auto outputbuffer_first_run = outputbuffer;
  415|    576|        std::ranges::fill(outputbuffer, canary2);
  416|    576|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    576|                                          src.size(), outputbuffer.data());
  418|       |
  419|    576|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 576]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    576|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 351, False: 225]
  |  Branch (423:29): [True: 339, False: 12]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    339|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    339|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 339]
  ------------------
  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|    339|        }
  440|    576|      }
  441|    576|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 339, False: 237]
  ------------------
  442|    576|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    192|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 75, False: 117]
  ------------------
  447|    225|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 225, False: 75]
  ------------------
  448|    225|        e.outputhash.clear();
  449|    225|      }
  450|     75|    }
  451|       |
  452|    192|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    192|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 192]
  ------------------
  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|    192|    } else {
  474|    192|      ret.implementations_agree = true;
  475|    192|    }
  476|    192|    return ret;
  477|    192|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    576|      const auto success = [](const ConversionResult& r) -> bool {
  402|    576|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    576|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    576|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    384|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_1clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    212|    +[](std::span<const char> chardata) {                                      \
  555|    212|      const auto c =                                                           \
  556|    212|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    212|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    212|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    212|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    212|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    212|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    212|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    212|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    212|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    212|  constexpr auto _size = _name.size();                                \
  |  | 1183|    212|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    212|  return _nameof; }()
  ------------------
  561|    212|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    212|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    212|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    212|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    212|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    212|  constexpr auto _size = _name.size();                                \
  |  | 1183|    212|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    212|  return _nameof; }()
  ------------------
  562|    212|      c.fuzz(chardata);                                                        \
  563|    212|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    383|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    383|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    383|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    383|    static const bool do_print_testcase =
  181|    383|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    383|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 383]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    383|    do {
  189|       |      // step 0 - is the input valid?
  190|    383|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    383|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 383]
  |  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|    383|                    From == UtfEncodings::UTF8) {
  198|    383|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 383]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    383|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    383|      const auto [output_length, length_agree] =
  204|    383|          calculate_length(from, inputisvalid);
  205|    383|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 383]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    383|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 89, False: 294]
  |  Branch (208:28): [True: 12, False: 77]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|     12|        return;
  211|     12|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    371|      const auto [written, outputs_agree] =
  215|    371|          do_conversion(from, output_length, inputisvalid);
  216|    371|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 371]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    371|      return;
  221|    371|    } 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|    383|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    383|  validation_result verify_valid_input(FromSpan src) const {
  231|    383|    validation_result ret{};
  232|       |
  233|    383|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    383|    const auto implementations = get_supported_implementations();
  235|    383|    std::vector<simdutf::result> results;
  236|    383|    results.reserve(implementations.size());
  237|       |
  238|  1.14k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.14k, False: 383]
  ------------------
  239|  1.14k|      results.push_back(
  240|  1.14k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.14k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.14k|      const bool validation2 =
  245|  1.14k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.14k|                      src.data(), src.size());
  247|  1.14k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.14k]
  ------------------
  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.14k|    }
  258|       |
  259|    383|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    383|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 383]
  ------------------
  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|    383|    } else {
  273|    383|      ret.implementations_agree = true;
  274|    383|    }
  275|    383|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    383|      return r.error == simdutf::SUCCESS;
  277|    383|    });
  278|    383|    return ret;
  279|    383|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    766|    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|    971|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    971|      return r.error == simdutf::SUCCESS;
  277|    971|    });
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    383|  bool count_the_input(FromSpan src) const {
  290|    383|    const auto implementations = get_supported_implementations();
  291|    383|    std::vector<std::size_t> results;
  292|    383|    results.reserve(implementations.size());
  293|       |
  294|  1.14k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.14k, False: 383]
  ------------------
  295|  1.14k|      std::size_t ret;
  296|  1.14k|      if constexpr (From == UtfEncodings::UTF16BE) {
  297|  1.14k|        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.14k|      results.push_back(ret);
  304|  1.14k|    }
  305|    383|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    383|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 383]
  ------------------
  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|    383|    return true;
  321|    383|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|    766|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    383|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    383|    length_result ret{};
  344|       |
  345|    383|    const auto implementations = get_supported_implementations();
  346|    383|    std::vector<std::size_t> results;
  347|    383|    results.reserve(implementations.size());
  348|       |
  349|  1.14k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.14k, False: 383]
  ------------------
  350|  1.14k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.14k|      results.push_back(len);
  352|  1.14k|      ret.length.push_back(len);
  353|  1.14k|    }
  354|       |
  355|    383|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    383|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 383]
  ------------------
  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|    383|    } else {
  375|    383|      ret.implementations_agree = true;
  376|    383|    }
  377|    383|    return ret;
  378|    383|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.14k|                                FromSpan src) const {
  331|  1.14k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.14k|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    766|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    371|                                  const bool inputisvalid) const {
  383|    371|    conversion_result ret{};
  384|       |
  385|    371|    const auto implementations = get_supported_implementations();
  386|       |
  387|    371|    std::vector<result<ConversionResult>> results;
  388|    371|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    371|    std::vector<std::vector<ToType>> outputbuffers;
  393|    371|    outputbuffers.reserve(implementations.size());
  394|  1.48k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.11k, False: 371]
  ------------------
  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: 882, False: 231]
  |  Branch (423:29): [True: 870, False: 12]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    870|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    870|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 870]
  ------------------
  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|    870|        }
  440|  1.11k|      }
  441|  1.11k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 870, False: 243]
  ------------------
  442|  1.11k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    371|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 77, False: 294]
  ------------------
  447|    231|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 231, False: 77]
  ------------------
  448|    231|        e.outputhash.clear();
  449|    231|      }
  450|     77|    }
  451|       |
  452|    371|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    371|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 371]
  ------------------
  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|    371|    } else {
  474|    371|      ret.implementations_agree = true;
  475|    371|    }
  476|    371|    return ret;
  477|    371|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  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|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  1.11k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    742|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_2clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     66|    +[](std::span<const char> chardata) {                                      \
  555|     66|      const auto c =                                                           \
  556|     66|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     66|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     66|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     66|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     66|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     66|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     66|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     66|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     66|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     66|  constexpr auto _size = _name.size();                                \
  |  | 1183|     66|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     66|  return _nameof; }()
  ------------------
  561|     66|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     66|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     66|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     66|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     66|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     66|  constexpr auto _size = _name.size();                                \
  |  | 1183|     66|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     66|  return _nameof; }()
  ------------------
  562|     66|      c.fuzz(chardata);                                                        \
  563|     66|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    213|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    213|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    213|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    213|    static const bool do_print_testcase =
  181|    213|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    213|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 213]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    213|    do {
  189|       |      // step 0 - is the input valid?
  190|    213|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    213|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 213]
  |  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|    213|                    From == UtfEncodings::UTF8) {
  198|    213|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 213]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    213|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    213|      const auto [output_length, length_agree] =
  204|    213|          calculate_length(from, inputisvalid);
  205|    213|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 213]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    213|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 94, False: 119]
  |  Branch (208:28): [True: 6, False: 88]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      6|        return;
  211|      6|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    207|      const auto [written, outputs_agree] =
  215|    207|          do_conversion(from, output_length, inputisvalid);
  216|    207|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 207]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    207|      return;
  221|    207|    } 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|    213|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    213|  validation_result verify_valid_input(FromSpan src) const {
  231|    213|    validation_result ret{};
  232|       |
  233|    213|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    213|    const auto implementations = get_supported_implementations();
  235|    213|    std::vector<simdutf::result> results;
  236|    213|    results.reserve(implementations.size());
  237|       |
  238|    639|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 639, False: 213]
  ------------------
  239|    639|      results.push_back(
  240|    639|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    639|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    639|      const bool validation2 =
  245|    639|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    639|                      src.data(), src.size());
  247|    639|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 639]
  ------------------
  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|    639|    }
  258|       |
  259|    213|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    213|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 213]
  ------------------
  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|    213|    } else {
  273|    213|      ret.implementations_agree = true;
  274|    213|    }
  275|    213|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    213|      return r.error == simdutf::SUCCESS;
  277|    213|    });
  278|    213|    return ret;
  279|    213|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    426|    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|    451|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    451|      return r.error == simdutf::SUCCESS;
  277|    451|    });
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    213|  bool count_the_input(FromSpan src) const {
  290|    213|    const auto implementations = get_supported_implementations();
  291|    213|    std::vector<std::size_t> results;
  292|    213|    results.reserve(implementations.size());
  293|       |
  294|    639|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 639, False: 213]
  ------------------
  295|    639|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|    639|      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|    639|        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|    639|      results.push_back(ret);
  304|    639|    }
  305|    213|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    213|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 213]
  ------------------
  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|    213|    return true;
  321|    213|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|    426|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    213|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    213|    length_result ret{};
  344|       |
  345|    213|    const auto implementations = get_supported_implementations();
  346|    213|    std::vector<std::size_t> results;
  347|    213|    results.reserve(implementations.size());
  348|       |
  349|    639|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 639, False: 213]
  ------------------
  350|    639|      const auto len = invoke_lengthcalc(impl, src);
  351|    639|      results.push_back(len);
  352|    639|      ret.length.push_back(len);
  353|    639|    }
  354|       |
  355|    213|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    213|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 213]
  ------------------
  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|    213|    } else {
  375|    213|      ret.implementations_agree = true;
  376|    213|    }
  377|    213|    return ret;
  378|    213|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    639|                                FromSpan src) const {
  331|    639|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    639|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    426|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    207|                                  const bool inputisvalid) const {
  383|    207|    conversion_result ret{};
  384|       |
  385|    207|    const auto implementations = get_supported_implementations();
  386|       |
  387|    207|    std::vector<result<ConversionResult>> results;
  388|    207|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    207|    std::vector<std::vector<ToType>> outputbuffers;
  393|    207|    outputbuffers.reserve(implementations.size());
  394|    828|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 621, False: 207]
  ------------------
  395|    621|      auto impl = implementations[i];
  396|    621|      const ToType canary1{42};
  397|    621|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    621|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    621|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    621|      const auto success = [](const ConversionResult& r) -> bool {
  402|    621|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    621|          return r != 0;
  404|    621|        } else {
  405|    621|          return r.error == simdutf::error_code::SUCCESS;
  406|    621|        }
  407|    621|      }(implret1);
  408|    621|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    621|      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|    621|        const ToType canary2{25};
  414|    621|        const auto outputbuffer_first_run = outputbuffer;
  415|    621|        std::ranges::fill(outputbuffer, canary2);
  416|    621|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    621|                                          src.size(), outputbuffer.data());
  418|       |
  419|    621|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 621]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    621|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 357, False: 264]
  |  Branch (423:29): [True: 345, False: 12]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    345|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    345|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 345]
  ------------------
  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|    345|        }
  440|    621|      }
  441|    621|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 345, False: 276]
  ------------------
  442|    621|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    207|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 88, False: 119]
  ------------------
  447|    264|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 264, False: 88]
  ------------------
  448|    264|        e.outputhash.clear();
  449|    264|      }
  450|     88|    }
  451|       |
  452|    207|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    207|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 207]
  ------------------
  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|    207|    } else {
  474|    207|      ret.implementations_agree = true;
  475|    207|    }
  476|    207|    return ret;
  477|    207|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    621|      const auto success = [](const ConversionResult& r) -> bool {
  402|    621|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    621|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    621|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    414|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_3clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    223|    +[](std::span<const char> chardata) {                                      \
  555|    223|      const auto c =                                                           \
  556|    223|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    223|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    223|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    223|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    223|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    223|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    223|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    223|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    223|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    223|  constexpr auto _size = _name.size();                                \
  |  | 1183|    223|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    223|  return _nameof; }()
  ------------------
  561|    223|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    223|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    223|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    223|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    223|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    223|  constexpr auto _size = _name.size();                                \
  |  | 1183|    223|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    223|  return _nameof; }()
  ------------------
  562|    223|      c.fuzz(chardata);                                                        \
  563|    223|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    428|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    428|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    428|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    428|    static const bool do_print_testcase =
  181|    428|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    428|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 428]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    428|    do {
  189|       |      // step 0 - is the input valid?
  190|    428|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    428|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 428]
  |  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|    428|                    From == UtfEncodings::UTF8) {
  198|    428|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 428]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    428|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    428|      const auto [output_length, length_agree] =
  204|    428|          calculate_length(from, inputisvalid);
  205|    428|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 428]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    428|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 115, False: 313]
  |  Branch (208:28): [True: 9, False: 106]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      9|        return;
  211|      9|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    419|      const auto [written, outputs_agree] =
  215|    419|          do_conversion(from, output_length, inputisvalid);
  216|    419|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 419]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    419|      return;
  221|    419|    } 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|    428|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    428|  validation_result verify_valid_input(FromSpan src) const {
  231|    428|    validation_result ret{};
  232|       |
  233|    428|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    428|    const auto implementations = get_supported_implementations();
  235|    428|    std::vector<simdutf::result> results;
  236|    428|    results.reserve(implementations.size());
  237|       |
  238|  1.28k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.28k, False: 428]
  ------------------
  239|  1.28k|      results.push_back(
  240|  1.28k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.28k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.28k|      const bool validation2 =
  245|  1.28k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.28k|                      src.data(), src.size());
  247|  1.28k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.28k]
  ------------------
  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.28k|    }
  258|       |
  259|    428|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    428|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 428]
  ------------------
  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|    428|    } else {
  273|    428|      ret.implementations_agree = true;
  274|    428|    }
  275|    428|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    428|      return r.error == simdutf::SUCCESS;
  277|    428|    });
  278|    428|    return ret;
  279|    428|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    856|    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.05k|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|  1.05k|      return r.error == simdutf::SUCCESS;
  277|  1.05k|    });
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    428|  bool count_the_input(FromSpan src) const {
  290|    428|    const auto implementations = get_supported_implementations();
  291|    428|    std::vector<std::size_t> results;
  292|    428|    results.reserve(implementations.size());
  293|       |
  294|  1.28k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.28k, False: 428]
  ------------------
  295|  1.28k|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|  1.28k|      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|  1.28k|        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.28k|      results.push_back(ret);
  304|  1.28k|    }
  305|    428|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    428|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 428]
  ------------------
  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|    428|    return true;
  321|    428|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|    856|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    428|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    428|    length_result ret{};
  344|       |
  345|    428|    const auto implementations = get_supported_implementations();
  346|    428|    std::vector<std::size_t> results;
  347|    428|    results.reserve(implementations.size());
  348|       |
  349|  1.28k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.28k, False: 428]
  ------------------
  350|  1.28k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.28k|      results.push_back(len);
  352|  1.28k|      ret.length.push_back(len);
  353|  1.28k|    }
  354|       |
  355|    428|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    428|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 428]
  ------------------
  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|    428|    } else {
  375|    428|      ret.implementations_agree = true;
  376|    428|    }
  377|    428|    return ret;
  378|    428|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.28k|                                FromSpan src) const {
  331|  1.28k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.28k|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    856|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    419|                                  const bool inputisvalid) const {
  383|    419|    conversion_result ret{};
  384|       |
  385|    419|    const auto implementations = get_supported_implementations();
  386|       |
  387|    419|    std::vector<result<ConversionResult>> results;
  388|    419|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    419|    std::vector<std::vector<ToType>> outputbuffers;
  393|    419|    outputbuffers.reserve(implementations.size());
  394|  1.67k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.25k, False: 419]
  ------------------
  395|  1.25k|      auto impl = implementations[i];
  396|  1.25k|      const ToType canary1{42};
  397|  1.25k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.25k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.25k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.25k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.25k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.25k|          return r != 0;
  404|  1.25k|        } else {
  405|  1.25k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.25k|        }
  407|  1.25k|      }(implret1);
  408|  1.25k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.25k|      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.25k|        const ToType canary2{25};
  414|  1.25k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.25k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.25k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.25k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.25k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.25k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.25k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 939, False: 318]
  |  Branch (423:29): [True: 930, False: 9]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    930|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    930|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 930]
  ------------------
  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|    930|        }
  440|  1.25k|      }
  441|  1.25k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 930, False: 327]
  ------------------
  442|  1.25k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    419|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 106, False: 313]
  ------------------
  447|    318|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 318, False: 106]
  ------------------
  448|    318|        e.outputhash.clear();
  449|    318|      }
  450|    106|    }
  451|       |
  452|    419|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    419|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 419]
  ------------------
  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|    419|    } else {
  474|    419|      ret.implementations_agree = true;
  475|    419|    }
  476|    419|    return ret;
  477|    419|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|  1.25k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.25k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.25k|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  1.25k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    838|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_4clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     65|    +[](std::span<const char> chardata) {                                      \
  555|     65|      const auto c =                                                           \
  556|     65|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     65|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     65|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     65|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     65|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     65|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     65|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     65|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     65|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     65|  constexpr auto _size = _name.size();                                \
  |  | 1183|     65|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     65|  return _nameof; }()
  ------------------
  561|     65|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     65|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     65|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     65|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     65|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     65|  constexpr auto _size = _name.size();                                \
  |  | 1183|     65|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     65|  return _nameof; }()
  ------------------
  562|     65|      c.fuzz(chardata);                                                        \
  563|     65|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    319|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    319|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    319|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    319|    static const bool do_print_testcase =
  181|    319|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    319|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 319]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    319|    do {
  189|       |      // step 0 - is the input valid?
  190|    319|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    319|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 319]
  |  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|    319|      const auto [output_length, length_agree] =
  204|    319|          calculate_length(from, inputisvalid);
  205|    319|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 319]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    319|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 187, False: 132]
  |  Branch (208:28): [True: 2, False: 185]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      2|        return;
  211|      2|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    317|      const auto [written, outputs_agree] =
  215|    317|          do_conversion(from, output_length, inputisvalid);
  216|    317|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 317]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    317|      return;
  221|    317|    } 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|    319|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    319|  validation_result verify_valid_input(FromSpan src) const {
  231|    319|    validation_result ret{};
  232|       |
  233|    319|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    319|    const auto implementations = get_supported_implementations();
  235|    319|    std::vector<simdutf::result> results;
  236|    319|    results.reserve(implementations.size());
  237|       |
  238|    957|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 957, False: 319]
  ------------------
  239|    957|      results.push_back(
  240|    957|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    957|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    957|      const bool validation2 =
  245|    957|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    957|                      src.data(), src.size());
  247|    957|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 957]
  ------------------
  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|    957|    }
  258|       |
  259|    319|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    319|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 319]
  ------------------
  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|    319|    } else {
  273|    319|      ret.implementations_agree = true;
  274|    319|    }
  275|    319|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    319|      return r.error == simdutf::SUCCESS;
  277|    319|    });
  278|    319|    return ret;
  279|    319|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    638|    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|    583|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    583|      return r.error == simdutf::SUCCESS;
  277|    583|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    319|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    319|    length_result ret{};
  344|       |
  345|    319|    const auto implementations = get_supported_implementations();
  346|    319|    std::vector<std::size_t> results;
  347|    319|    results.reserve(implementations.size());
  348|       |
  349|    957|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 957, False: 319]
  ------------------
  350|    957|      const auto len = invoke_lengthcalc(impl, src);
  351|    957|      results.push_back(len);
  352|    957|      ret.length.push_back(len);
  353|    957|    }
  354|       |
  355|    319|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    319|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 319]
  ------------------
  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|    319|    } else {
  375|    319|      ret.implementations_agree = true;
  376|    319|    }
  377|    319|    return ret;
  378|    319|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    957|                                FromSpan src) const {
  331|    957|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    957|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    638|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    317|                                  const bool inputisvalid) const {
  383|    317|    conversion_result ret{};
  384|       |
  385|    317|    const auto implementations = get_supported_implementations();
  386|       |
  387|    317|    std::vector<result<ConversionResult>> results;
  388|    317|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    317|    std::vector<std::vector<ToType>> outputbuffers;
  393|    317|    outputbuffers.reserve(implementations.size());
  394|  1.26k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 951, False: 317]
  ------------------
  395|    951|      auto impl = implementations[i];
  396|    951|      const ToType canary1{42};
  397|    951|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    951|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    951|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    951|      const auto success = [](const ConversionResult& r) -> bool {
  402|    951|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    951|          return r != 0;
  404|    951|        } else {
  405|    951|          return r.error == simdutf::error_code::SUCCESS;
  406|    951|        }
  407|    951|      }(implret1);
  408|    951|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    951|      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|    951|        const ToType canary2{25};
  414|    951|        const auto outputbuffer_first_run = outputbuffer;
  415|    951|        std::ranges::fill(outputbuffer, canary2);
  416|    951|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    951|                                          src.size(), outputbuffer.data());
  418|       |
  419|    951|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 951]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    951|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 396, False: 555]
  |  Branch (423:29): [True: 387, False: 9]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    387|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    387|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 387]
  ------------------
  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|    387|        }
  440|    951|      }
  441|    951|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 387, False: 564]
  ------------------
  442|    951|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    317|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 185, False: 132]
  ------------------
  447|    555|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 555, False: 185]
  ------------------
  448|    555|        e.outputhash.clear();
  449|    555|      }
  450|    185|    }
  451|       |
  452|    317|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    317|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 317]
  ------------------
  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|    317|    } else {
  474|    317|      ret.implementations_agree = true;
  475|    317|    }
  476|    317|    return ret;
  477|    317|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    951|      const auto success = [](const ConversionResult& r) -> bool {
  402|    951|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    951|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    951|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    634|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_5clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    128|    +[](std::span<const char> chardata) {                                      \
  555|    128|      const auto c =                                                           \
  556|    128|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    128|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    128|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    128|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    128|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    128|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    128|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    128|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    128|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    128|  constexpr auto _size = _name.size();                                \
  |  | 1183|    128|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    128|  return _nameof; }()
  ------------------
  561|    128|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    128|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    128|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    128|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    128|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    128|  constexpr auto _size = _name.size();                                \
  |  | 1183|    128|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    128|  return _nameof; }()
  ------------------
  562|    128|      c.fuzz(chardata);                                                        \
  563|    128|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    376|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    376|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    376|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    376|    static const bool do_print_testcase =
  181|    376|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    376|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 376]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    376|    do {
  189|       |      // step 0 - is the input valid?
  190|    376|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    376|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 376]
  |  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|    376|      const auto [output_length, length_agree] =
  204|    376|          calculate_length(from, inputisvalid);
  205|    376|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 376]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    376|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 181, False: 195]
  |  Branch (208:28): [True: 7, False: 174]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      7|        return;
  211|      7|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    369|      const auto [written, outputs_agree] =
  215|    369|          do_conversion(from, output_length, inputisvalid);
  216|    369|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 369]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    369|      return;
  221|    369|    } 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|    376|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    376|  validation_result verify_valid_input(FromSpan src) const {
  231|    376|    validation_result ret{};
  232|       |
  233|    376|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    376|    const auto implementations = get_supported_implementations();
  235|    376|    std::vector<simdutf::result> results;
  236|    376|    results.reserve(implementations.size());
  237|       |
  238|  1.12k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.12k, False: 376]
  ------------------
  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|    376|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    376|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 376]
  ------------------
  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|    376|    } else {
  273|    376|      ret.implementations_agree = true;
  274|    376|    }
  275|    376|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    376|      return r.error == simdutf::SUCCESS;
  277|    376|    });
  278|    376|    return ret;
  279|    376|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    752|    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|    766|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    766|      return r.error == simdutf::SUCCESS;
  277|    766|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    376|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    376|    length_result ret{};
  344|       |
  345|    376|    const auto implementations = get_supported_implementations();
  346|    376|    std::vector<std::size_t> results;
  347|    376|    results.reserve(implementations.size());
  348|       |
  349|  1.12k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.12k, False: 376]
  ------------------
  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|    376|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    376|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 376]
  ------------------
  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|    376|    } else {
  375|    376|      ret.implementations_agree = true;
  376|    376|    }
  377|    376|    return ret;
  378|    376|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_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_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    752|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    369|                                  const bool inputisvalid) const {
  383|    369|    conversion_result ret{};
  384|       |
  385|    369|    const auto implementations = get_supported_implementations();
  386|       |
  387|    369|    std::vector<result<ConversionResult>> results;
  388|    369|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    369|    std::vector<std::vector<ToType>> outputbuffers;
  393|    369|    outputbuffers.reserve(implementations.size());
  394|  1.47k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.10k, False: 369]
  ------------------
  395|  1.10k|      auto impl = implementations[i];
  396|  1.10k|      const ToType canary1{42};
  397|  1.10k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.10k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.10k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.10k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.10k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.10k|          return r != 0;
  404|  1.10k|        } else {
  405|  1.10k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.10k|        }
  407|  1.10k|      }(implret1);
  408|  1.10k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.10k|      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.10k|        const ToType canary2{25};
  414|  1.10k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.10k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.10k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.10k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.10k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.10k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.10k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 585, False: 522]
  |  Branch (423:29): [True: 573, False: 12]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    573|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    573|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 573]
  ------------------
  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|    573|        }
  440|  1.10k|      }
  441|  1.10k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 573, False: 534]
  ------------------
  442|  1.10k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    369|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 174, False: 195]
  ------------------
  447|    522|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 522, False: 174]
  ------------------
  448|    522|        e.outputhash.clear();
  449|    522|      }
  450|    174|    }
  451|       |
  452|    369|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    369|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 369]
  ------------------
  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|    369|    } else {
  474|    369|      ret.implementations_agree = true;
  475|    369|    }
  476|    369|    return ret;
  477|    369|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|  1.10k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.10k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.10k|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  1.10k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    738|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_6clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    120|    +[](std::span<const char> chardata) {                                      \
  555|    120|      const auto c =                                                           \
  556|    120|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    120|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    120|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    120|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    120|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    120|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    120|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    120|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    120|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    120|  constexpr auto _size = _name.size();                                \
  |  | 1183|    120|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    120|  return _nameof; }()
  ------------------
  561|    120|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    120|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    120|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    120|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    120|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    120|  constexpr auto _size = _name.size();                                \
  |  | 1183|    120|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    120|  return _nameof; }()
  ------------------
  562|    120|      c.fuzz(chardata);                                                        \
  563|    120|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    438|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    438|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    438|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    438|    static const bool do_print_testcase =
  181|    438|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    438|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 438]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    438|    do {
  189|       |      // step 0 - is the input valid?
  190|    438|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    438|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 438]
  |  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|    438|      const auto [output_length, length_agree] =
  204|    438|          calculate_length(from, inputisvalid);
  205|    438|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 438]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    438|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 274, False: 164]
  |  Branch (208:28): [True: 8, False: 266]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      8|        return;
  211|      8|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    430|      const auto [written, outputs_agree] =
  215|    430|          do_conversion(from, output_length, inputisvalid);
  216|    430|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 430]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    430|      return;
  221|    430|    } 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|    438|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    438|  validation_result verify_valid_input(FromSpan src) const {
  231|    438|    validation_result ret{};
  232|       |
  233|    438|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    438|    const auto implementations = get_supported_implementations();
  235|    438|    std::vector<simdutf::result> results;
  236|    438|    results.reserve(implementations.size());
  237|       |
  238|  1.31k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.31k, False: 438]
  ------------------
  239|  1.31k|      results.push_back(
  240|  1.31k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.31k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.31k|      const bool validation2 =
  245|  1.31k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.31k|                      src.data(), src.size());
  247|  1.31k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.31k]
  ------------------
  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.31k|    }
  258|       |
  259|    438|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    438|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 438]
  ------------------
  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|    438|    } else {
  273|    438|      ret.implementations_agree = true;
  274|    438|    }
  275|    438|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    438|      return r.error == simdutf::SUCCESS;
  277|    438|    });
  278|    438|    return ret;
  279|    438|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    876|    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|    766|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    766|      return r.error == simdutf::SUCCESS;
  277|    766|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    438|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    438|    length_result ret{};
  344|       |
  345|    438|    const auto implementations = get_supported_implementations();
  346|    438|    std::vector<std::size_t> results;
  347|    438|    results.reserve(implementations.size());
  348|       |
  349|  1.31k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.31k, False: 438]
  ------------------
  350|  1.31k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.31k|      results.push_back(len);
  352|  1.31k|      ret.length.push_back(len);
  353|  1.31k|    }
  354|       |
  355|    438|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    438|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 438]
  ------------------
  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|    438|    } else {
  375|    438|      ret.implementations_agree = true;
  376|    438|    }
  377|    438|    return ret;
  378|    438|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.31k|                                FromSpan src) const {
  331|  1.31k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.31k|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    876|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    430|                                  const bool inputisvalid) const {
  383|    430|    conversion_result ret{};
  384|       |
  385|    430|    const auto implementations = get_supported_implementations();
  386|       |
  387|    430|    std::vector<result<ConversionResult>> results;
  388|    430|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    430|    std::vector<std::vector<ToType>> outputbuffers;
  393|    430|    outputbuffers.reserve(implementations.size());
  394|  1.72k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.29k, False: 430]
  ------------------
  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: 492, False: 798]
  |  Branch (423:29): [True: 483, False: 9]
  ------------------
  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|  1.29k|      }
  441|  1.29k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 483, False: 807]
  ------------------
  442|  1.29k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    430|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 266, False: 164]
  ------------------
  447|    798|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 798, False: 266]
  ------------------
  448|    798|        e.outputhash.clear();
  449|    798|      }
  450|    266|    }
  451|       |
  452|    430|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    430|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 430]
  ------------------
  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|    430|    } else {
  474|    430|      ret.implementations_agree = true;
  475|    430|    }
  476|    430|    return ret;
  477|    430|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  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|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  1.29k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    860|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_7clENSt3__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|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    623|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    623|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    623|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    623|    static const bool do_print_testcase =
  181|    623|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    623|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 623]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    623|    do {
  189|       |      // step 0 - is the input valid?
  190|    623|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    623|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 623]
  |  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|    623|                    From == UtfEncodings::UTF8) {
  198|    623|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 623]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    623|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    623|      const auto [output_length, length_agree] =
  204|    623|          calculate_length(from, inputisvalid);
  205|    623|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 623]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    623|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 299, False: 324]
  |  Branch (208:28): [True: 16, False: 283]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|     16|        return;
  211|     16|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    607|      const auto [written, outputs_agree] =
  215|    607|          do_conversion(from, output_length, inputisvalid);
  216|    607|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 607]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    607|      return;
  221|    607|    } 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|    623|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    623|  validation_result verify_valid_input(FromSpan src) const {
  231|    623|    validation_result ret{};
  232|       |
  233|    623|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    623|    const auto implementations = get_supported_implementations();
  235|    623|    std::vector<simdutf::result> results;
  236|    623|    results.reserve(implementations.size());
  237|       |
  238|  1.86k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.86k, False: 623]
  ------------------
  239|  1.86k|      results.push_back(
  240|  1.86k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.86k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.86k|      const bool validation2 =
  245|  1.86k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.86k|                      src.data(), src.size());
  247|  1.86k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.86k]
  ------------------
  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.86k|    }
  258|       |
  259|    623|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    623|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 623]
  ------------------
  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|    623|    } else {
  273|    623|      ret.implementations_agree = true;
  274|    623|    }
  275|    623|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    623|      return r.error == simdutf::SUCCESS;
  277|    623|    });
  278|    623|    return ret;
  279|    623|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|  1.24k|    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.27k|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|  1.27k|      return r.error == simdutf::SUCCESS;
  277|  1.27k|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    623|  bool count_the_input(FromSpan src) const {
  290|    623|    const auto implementations = get_supported_implementations();
  291|    623|    std::vector<std::size_t> results;
  292|    623|    results.reserve(implementations.size());
  293|       |
  294|  1.86k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.86k, False: 623]
  ------------------
  295|  1.86k|      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.86k|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|  1.86k|        ret = impl->count_utf8(src.data(), src.size());
  302|  1.86k|      }
  303|  1.86k|      results.push_back(ret);
  304|  1.86k|    }
  305|    623|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    623|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 623]
  ------------------
  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|    623|    return true;
  321|    623|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|  1.24k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    623|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    623|    length_result ret{};
  344|       |
  345|    623|    const auto implementations = get_supported_implementations();
  346|    623|    std::vector<std::size_t> results;
  347|    623|    results.reserve(implementations.size());
  348|       |
  349|  1.86k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.86k, False: 623]
  ------------------
  350|  1.86k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.86k|      results.push_back(len);
  352|  1.86k|      ret.length.push_back(len);
  353|  1.86k|    }
  354|       |
  355|    623|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    623|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 623]
  ------------------
  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|    623|    } else {
  375|    623|      ret.implementations_agree = true;
  376|    623|    }
  377|    623|    return ret;
  378|    623|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.86k|                                FromSpan src) const {
  331|  1.86k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.86k|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|  1.24k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    607|                                  const bool inputisvalid) const {
  383|    607|    conversion_result ret{};
  384|       |
  385|    607|    const auto implementations = get_supported_implementations();
  386|       |
  387|    607|    std::vector<result<ConversionResult>> results;
  388|    607|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    607|    std::vector<std::vector<ToType>> outputbuffers;
  393|    607|    outputbuffers.reserve(implementations.size());
  394|  2.42k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.82k, False: 607]
  ------------------
  395|  1.82k|      auto impl = implementations[i];
  396|  1.82k|      const ToType canary1{42};
  397|  1.82k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.82k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.82k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.82k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.82k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.82k|          return r != 0;
  404|  1.82k|        } else {
  405|  1.82k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.82k|        }
  407|  1.82k|      }(implret1);
  408|  1.82k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.82k|      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.82k|        const ToType canary2{25};
  414|  1.82k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.82k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.82k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.82k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.82k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.82k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.82k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 972, False: 849]
  |  Branch (423:29): [True: 966, False: 6]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    966|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    966|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 966]
  ------------------
  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|    966|        }
  440|  1.82k|      }
  441|  1.82k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 966, False: 855]
  ------------------
  442|  1.82k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    607|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 283, False: 324]
  ------------------
  447|    849|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 849, False: 283]
  ------------------
  448|    849|        e.outputhash.clear();
  449|    849|      }
  450|    283|    }
  451|       |
  452|    607|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    607|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 607]
  ------------------
  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|    607|    } else {
  474|    607|      ret.implementations_agree = true;
  475|    607|    }
  476|    607|    return ret;
  477|    607|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|  1.82k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.82k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.82k|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  1.82k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|  1.21k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_8clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    226|    +[](std::span<const char> chardata) {                                      \
  555|    226|      const auto c =                                                           \
  556|    226|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    226|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    226|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    226|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    226|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    226|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    226|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    226|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    226|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    226|  constexpr auto _size = _name.size();                                \
  |  | 1183|    226|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    226|  return _nameof; }()
  ------------------
  561|    226|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    226|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    226|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    226|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    226|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    226|  constexpr auto _size = _name.size();                                \
  |  | 1183|    226|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    226|  return _nameof; }()
  ------------------
  562|    226|      c.fuzz(chardata);                                                        \
  563|    226|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    603|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    603|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    603|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    603|    static const bool do_print_testcase =
  181|    603|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    603|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 603]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    603|    do {
  189|       |      // step 0 - is the input valid?
  190|    603|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    603|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 603]
  |  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|    603|                    From == UtfEncodings::UTF8) {
  198|    603|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 603]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    603|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    603|      const auto [output_length, length_agree] =
  204|    603|          calculate_length(from, inputisvalid);
  205|    603|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 603]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    603|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 266, False: 337]
  |  Branch (208:28): [True: 7, False: 259]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      7|        return;
  211|      7|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    596|      const auto [written, outputs_agree] =
  215|    596|          do_conversion(from, output_length, inputisvalid);
  216|    596|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 596]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    596|      return;
  221|    596|    } 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|    603|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    603|  validation_result verify_valid_input(FromSpan src) const {
  231|    603|    validation_result ret{};
  232|       |
  233|    603|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    603|    const auto implementations = get_supported_implementations();
  235|    603|    std::vector<simdutf::result> results;
  236|    603|    results.reserve(implementations.size());
  237|       |
  238|  1.80k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.80k, False: 603]
  ------------------
  239|  1.80k|      results.push_back(
  240|  1.80k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.80k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.80k|      const bool validation2 =
  245|  1.80k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.80k|                      src.data(), src.size());
  247|  1.80k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.80k]
  ------------------
  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.80k|    }
  258|       |
  259|    603|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    603|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 603]
  ------------------
  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|    603|    } else {
  273|    603|      ret.implementations_agree = true;
  274|    603|    }
  275|    603|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    603|      return r.error == simdutf::SUCCESS;
  277|    603|    });
  278|    603|    return ret;
  279|    603|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|  1.20k|    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.27k|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|  1.27k|      return r.error == simdutf::SUCCESS;
  277|  1.27k|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    603|  bool count_the_input(FromSpan src) const {
  290|    603|    const auto implementations = get_supported_implementations();
  291|    603|    std::vector<std::size_t> results;
  292|    603|    results.reserve(implementations.size());
  293|       |
  294|  1.80k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.80k, False: 603]
  ------------------
  295|  1.80k|      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.80k|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|  1.80k|        ret = impl->count_utf8(src.data(), src.size());
  302|  1.80k|      }
  303|  1.80k|      results.push_back(ret);
  304|  1.80k|    }
  305|    603|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    603|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 603]
  ------------------
  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|    603|    return true;
  321|    603|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|  1.20k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    603|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    603|    length_result ret{};
  344|       |
  345|    603|    const auto implementations = get_supported_implementations();
  346|    603|    std::vector<std::size_t> results;
  347|    603|    results.reserve(implementations.size());
  348|       |
  349|  1.80k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.80k, False: 603]
  ------------------
  350|  1.80k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.80k|      results.push_back(len);
  352|  1.80k|      ret.length.push_back(len);
  353|  1.80k|    }
  354|       |
  355|    603|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    603|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 603]
  ------------------
  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|    603|    } else {
  375|    603|      ret.implementations_agree = true;
  376|    603|    }
  377|    603|    return ret;
  378|    603|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.80k|                                FromSpan src) const {
  331|  1.80k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.80k|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|  1.20k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    596|                                  const bool inputisvalid) const {
  383|    596|    conversion_result ret{};
  384|       |
  385|    596|    const auto implementations = get_supported_implementations();
  386|       |
  387|    596|    std::vector<result<ConversionResult>> results;
  388|    596|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    596|    std::vector<std::vector<ToType>> outputbuffers;
  393|    596|    outputbuffers.reserve(implementations.size());
  394|  2.38k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.78k, False: 596]
  ------------------
  395|  1.78k|      auto impl = implementations[i];
  396|  1.78k|      const ToType canary1{42};
  397|  1.78k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.78k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.78k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.78k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.78k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.78k|          return r != 0;
  404|  1.78k|        } else {
  405|  1.78k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.78k|        }
  407|  1.78k|      }(implret1);
  408|  1.78k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.78k|      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.78k|        const ToType canary2{25};
  414|  1.78k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.78k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.78k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.78k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.78k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.78k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.78k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 1.01k, False: 777]
  |  Branch (423:29): [True: 1.00k, False: 9]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|  1.00k|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|  1.00k|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 1.00k]
  ------------------
  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.00k|        }
  440|  1.78k|      }
  441|  1.78k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 1.00k, False: 786]
  ------------------
  442|  1.78k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    596|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 259, False: 337]
  ------------------
  447|    777|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 777, False: 259]
  ------------------
  448|    777|        e.outputhash.clear();
  449|    777|      }
  450|    259|    }
  451|       |
  452|    596|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    596|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 596]
  ------------------
  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|    596|    } else {
  474|    596|      ret.implementations_agree = true;
  475|    596|    }
  476|    596|    return ret;
  477|    596|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|  1.78k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.78k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.78k|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  1.78k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|  1.19k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK3$_9clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    234|    +[](std::span<const char> chardata) {                                      \
  555|    234|      const auto c =                                                           \
  556|    234|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    234|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    234|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    234|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    234|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    234|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    234|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    234|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    234|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    234|  constexpr auto _size = _name.size();                                \
  |  | 1183|    234|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    234|  return _nameof; }()
  ------------------
  561|    234|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    234|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    234|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    234|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    234|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    234|  constexpr auto _size = _name.size();                                \
  |  | 1183|    234|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    234|  return _nameof; }()
  ------------------
  562|    234|      c.fuzz(chardata);                                                        \
  563|    234|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    613|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    613|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    613|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    613|    static const bool do_print_testcase =
  181|    613|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    613|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 613]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    613|    do {
  189|       |      // step 0 - is the input valid?
  190|    613|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    613|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 613]
  |  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|    613|                    From == UtfEncodings::UTF8) {
  198|    613|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 613]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    613|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    613|      const auto [output_length, length_agree] =
  204|    613|          calculate_length(from, inputisvalid);
  205|    613|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 613]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    613|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 245, False: 368]
  |  Branch (208:28): [True: 7, False: 238]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      7|        return;
  211|      7|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    606|      const auto [written, outputs_agree] =
  215|    606|          do_conversion(from, output_length, inputisvalid);
  216|    606|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 606]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    606|      return;
  221|    606|    } 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|    613|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    613|  validation_result verify_valid_input(FromSpan src) const {
  231|    613|    validation_result ret{};
  232|       |
  233|    613|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    613|    const auto implementations = get_supported_implementations();
  235|    613|    std::vector<simdutf::result> results;
  236|    613|    results.reserve(implementations.size());
  237|       |
  238|  1.83k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.83k, False: 613]
  ------------------
  239|  1.83k|      results.push_back(
  240|  1.83k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.83k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.83k|      const bool validation2 =
  245|  1.83k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.83k|                      src.data(), src.size());
  247|  1.83k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.83k]
  ------------------
  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.83k|    }
  258|       |
  259|    613|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    613|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 613]
  ------------------
  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|    613|    } else {
  273|    613|      ret.implementations_agree = true;
  274|    613|    }
  275|    613|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    613|      return r.error == simdutf::SUCCESS;
  277|    613|    });
  278|    613|    return ret;
  279|    613|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|  1.22k|    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.34k|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|  1.34k|      return r.error == simdutf::SUCCESS;
  277|  1.34k|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    613|  bool count_the_input(FromSpan src) const {
  290|    613|    const auto implementations = get_supported_implementations();
  291|    613|    std::vector<std::size_t> results;
  292|    613|    results.reserve(implementations.size());
  293|       |
  294|  1.83k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.83k, False: 613]
  ------------------
  295|  1.83k|      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.83k|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|  1.83k|        ret = impl->count_utf8(src.data(), src.size());
  302|  1.83k|      }
  303|  1.83k|      results.push_back(ret);
  304|  1.83k|    }
  305|    613|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    613|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 613]
  ------------------
  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|    613|    return true;
  321|    613|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|  1.22k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    613|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    613|    length_result ret{};
  344|       |
  345|    613|    const auto implementations = get_supported_implementations();
  346|    613|    std::vector<std::size_t> results;
  347|    613|    results.reserve(implementations.size());
  348|       |
  349|  1.83k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.83k, False: 613]
  ------------------
  350|  1.83k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.83k|      results.push_back(len);
  352|  1.83k|      ret.length.push_back(len);
  353|  1.83k|    }
  354|       |
  355|    613|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    613|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 613]
  ------------------
  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|    613|    } else {
  375|    613|      ret.implementations_agree = true;
  376|    613|    }
  377|    613|    return ret;
  378|    613|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.83k|                                FromSpan src) const {
  331|  1.83k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.83k|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|  1.22k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    606|                                  const bool inputisvalid) const {
  383|    606|    conversion_result ret{};
  384|       |
  385|    606|    const auto implementations = get_supported_implementations();
  386|       |
  387|    606|    std::vector<result<ConversionResult>> results;
  388|    606|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    606|    std::vector<std::vector<ToType>> outputbuffers;
  393|    606|    outputbuffers.reserve(implementations.size());
  394|  2.42k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.81k, False: 606]
  ------------------
  395|  1.81k|      auto impl = implementations[i];
  396|  1.81k|      const ToType canary1{42};
  397|  1.81k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.81k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.81k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.81k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.81k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.81k|          return r != 0;
  404|  1.81k|        } else {
  405|  1.81k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.81k|        }
  407|  1.81k|      }(implret1);
  408|  1.81k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.81k|      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.81k|        const ToType canary2{25};
  414|  1.81k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.81k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.81k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.81k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.81k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.81k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.81k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 1.10k, False: 714]
  |  Branch (423:29): [True: 1.09k, False: 9]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|  1.09k|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|  1.09k|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 1.09k]
  ------------------
  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.09k|        }
  440|  1.81k|      }
  441|  1.81k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 1.09k, False: 723]
  ------------------
  442|  1.81k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    606|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 238, False: 368]
  ------------------
  447|    714|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 714, False: 238]
  ------------------
  448|    714|        e.outputhash.clear();
  449|    714|      }
  450|    238|    }
  451|       |
  452|    606|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    606|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 606]
  ------------------
  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|    606|    } else {
  474|    606|      ret.implementations_agree = true;
  475|    606|    }
  476|    606|    return ret;
  477|    606|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|  1.81k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.81k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.81k|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|  1.81k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|  1.21k|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_10clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     51|    +[](std::span<const char> chardata) {                                      \
  555|     51|      const auto c =                                                           \
  556|     51|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     51|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     51|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     51|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     51|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     51|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     51|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     51|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     51|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     51|  constexpr auto _size = _name.size();                                \
  |  | 1183|     51|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     51|  return _nameof; }()
  ------------------
  561|     51|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     51|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     51|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     51|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     51|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     51|  constexpr auto _size = _name.size();                                \
  |  | 1183|     51|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     51|  return _nameof; }()
  ------------------
  562|     51|      c.fuzz(chardata);                                                        \
  563|     51|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|     51|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|     51|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|     51|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|     51|    static const bool do_print_testcase =
  181|     51|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|     51|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 51]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|     51|    do {
  189|       |      // step 0 - is the input valid?
  190|     51|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|     51|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 51]
  |  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|     51|                    From == UtfEncodings::UTF8) {
  198|     51|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 51]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|     51|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|     51|      const auto [output_length, length_agree] =
  204|     51|          calculate_length(from, inputisvalid);
  205|     51|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 51]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|     51|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 12, False: 39]
  |  Branch (208:28): [True: 0, False: 12]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|     51|      const auto [written, outputs_agree] =
  215|     51|          do_conversion(from, output_length, inputisvalid);
  216|     51|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 51]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|     51|      return;
  221|     51|    } 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|     51|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEE:
  230|     51|  validation_result verify_valid_input(FromSpan src) const {
  231|     51|    validation_result ret{};
  232|       |
  233|     51|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|     51|    const auto implementations = get_supported_implementations();
  235|     51|    std::vector<simdutf::result> results;
  236|     51|    results.reserve(implementations.size());
  237|       |
  238|    153|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 153, False: 51]
  ------------------
  239|    153|      results.push_back(
  240|    153|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    153|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    153|      const bool validation2 =
  245|    153|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    153|                      src.data(), src.size());
  247|    153|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 153]
  ------------------
  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|    153|    }
  258|       |
  259|     51|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|     51|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 51]
  ------------------
  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|     51|    } else {
  273|     51|      ret.implementations_agree = true;
  274|     51|    }
  275|     51|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|     51|      return r.error == simdutf::SUCCESS;
  277|     51|    });
  278|     51|    return ret;
  279|     51|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    102|    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|    129|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    129|      return r.error == simdutf::SUCCESS;
  277|    129|    });
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE15count_the_inputENSt3__14spanIS5_Lm18446744073709551615EEE:
  289|     51|  bool count_the_input(FromSpan src) const {
  290|     51|    const auto implementations = get_supported_implementations();
  291|     51|    std::vector<std::size_t> results;
  292|     51|    results.reserve(implementations.size());
  293|       |
  294|    153|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 153, False: 51]
  ------------------
  295|    153|      std::size_t ret;
  296|    153|      if constexpr (From == UtfEncodings::UTF16BE) {
  297|    153|        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|    153|      results.push_back(ret);
  304|    153|    }
  305|     51|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|     51|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 51]
  ------------------
  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|     51|    return true;
  321|     51|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE15count_the_inputENSt3__14spanIS5_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|    102|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEb:
  342|     51|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|     51|    length_result ret{};
  344|       |
  345|     51|    const auto implementations = get_supported_implementations();
  346|     51|    std::vector<std::size_t> results;
  347|     51|    results.reserve(implementations.size());
  348|       |
  349|    153|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 153, False: 51]
  ------------------
  350|    153|      const auto len = invoke_lengthcalc(impl, src);
  351|    153|      results.push_back(len);
  352|    153|      ret.length.push_back(len);
  353|    153|    }
  354|       |
  355|     51|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|     51|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 51]
  ------------------
  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|     51|    } else {
  375|     51|      ret.implementations_agree = true;
  376|     51|    }
  377|     51|    return ret;
  378|     51|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSE_NSt3__14spanIS5_Lm18446744073709551615EEE:
  338|    153|                                FromSpan src) const {
  339|    153|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    153|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    102|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|     51|                                  const bool inputisvalid) const {
  383|     51|    conversion_result ret{};
  384|       |
  385|     51|    const auto implementations = get_supported_implementations();
  386|       |
  387|     51|    std::vector<result<ConversionResult>> results;
  388|     51|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|     51|    std::vector<std::vector<ToType>> outputbuffers;
  393|     51|    outputbuffers.reserve(implementations.size());
  394|    204|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 153, False: 51]
  ------------------
  395|    153|      auto impl = implementations[i];
  396|    153|      const ToType canary1{42};
  397|    153|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    153|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    153|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    153|      const auto success = [](const ConversionResult& r) -> bool {
  402|    153|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    153|          return r != 0;
  404|    153|        } else {
  405|    153|          return r.error == simdutf::error_code::SUCCESS;
  406|    153|        }
  407|    153|      }(implret1);
  408|    153|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    153|      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|    153|        const ToType canary2{25};
  414|    153|        const auto outputbuffer_first_run = outputbuffer;
  415|    153|        std::ranges::fill(outputbuffer, canary2);
  416|    153|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    153|                                          src.size(), outputbuffer.data());
  418|       |
  419|    153|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 153]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    153|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 117, False: 36]
  |  Branch (423:29): [True: 60, False: 57]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|     60|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|     60|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 60]
  ------------------
  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|     60|        }
  440|    153|      }
  441|    153|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 60, False: 93]
  ------------------
  442|    153|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|     51|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 12, False: 39]
  ------------------
  447|     36|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 36, False: 12]
  ------------------
  448|     36|        e.outputhash.clear();
  449|     36|      }
  450|     12|    }
  451|       |
  452|     51|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|     51|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 51]
  ------------------
  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|     51|    } else {
  474|     51|      ret.implementations_agree = true;
  475|     51|    }
  476|     51|    return ret;
  477|     51|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    153|      const auto success = [](const ConversionResult& r) -> bool {
  402|    153|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    153|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    153|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings0ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    102|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_11clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    119|    +[](std::span<const char> chardata) {                                      \
  555|    119|      const auto c =                                                           \
  556|    119|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    119|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    119|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    119|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    119|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    119|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    119|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    119|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    119|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    119|  constexpr auto _size = _name.size();                                \
  |  | 1183|    119|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    119|  return _nameof; }()
  ------------------
  561|    119|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    119|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    119|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    119|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    119|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    119|  constexpr auto _size = _name.size();                                \
  |  | 1183|    119|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    119|  return _nameof; }()
  ------------------
  562|    119|      c.fuzz(chardata);                                                        \
  563|    119|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_12clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    171|    +[](std::span<const char> chardata) {                                      \
  555|    171|      const auto c =                                                           \
  556|    171|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    171|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    171|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    171|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    171|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    171|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    171|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    171|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    171|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    171|  constexpr auto _size = _name.size();                                \
  |  | 1183|    171|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    171|  return _nameof; }()
  ------------------
  561|    171|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    171|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    171|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    171|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    171|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    171|  constexpr auto _size = _name.size();                                \
  |  | 1183|    171|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    171|  return _nameof; }()
  ------------------
  562|    171|      c.fuzz(chardata);                                                        \
  563|    171|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_13clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     66|    +[](std::span<const char> chardata) {                                      \
  555|     66|      const auto c =                                                           \
  556|     66|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     66|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     66|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     66|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     66|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     66|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     66|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     66|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     66|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     66|  constexpr auto _size = _name.size();                                \
  |  | 1183|     66|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     66|  return _nameof; }()
  ------------------
  561|     66|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     66|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     66|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     66|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     66|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     66|  constexpr auto _size = _name.size();                                \
  |  | 1183|     66|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     66|  return _nameof; }()
  ------------------
  562|     66|      c.fuzz(chardata);                                                        \
  563|     66|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|     66|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|     66|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|     66|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|     66|    static const bool do_print_testcase =
  181|     66|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|     66|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 66]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|     66|    do {
  189|       |      // step 0 - is the input valid?
  190|     66|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|     66|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 66]
  |  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|     66|                    From == UtfEncodings::UTF8) {
  198|     66|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 66]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|     66|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|     66|      const auto [output_length, length_agree] =
  204|     66|          calculate_length(from, inputisvalid);
  205|     66|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 66]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|     66|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 22, False: 44]
  |  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|     66|      const auto [written, outputs_agree] =
  215|     66|          do_conversion(from, output_length, inputisvalid);
  216|     66|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 66]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|     66|      return;
  221|     66|    } 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|     66|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEE:
  230|     66|  validation_result verify_valid_input(FromSpan src) const {
  231|     66|    validation_result ret{};
  232|       |
  233|     66|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|     66|    const auto implementations = get_supported_implementations();
  235|     66|    std::vector<simdutf::result> results;
  236|     66|    results.reserve(implementations.size());
  237|       |
  238|    198|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 198, False: 66]
  ------------------
  239|    198|      results.push_back(
  240|    198|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    198|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    198|      const bool validation2 =
  245|    198|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    198|                      src.data(), src.size());
  247|    198|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 198]
  ------------------
  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|    198|    }
  258|       |
  259|     66|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|     66|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 66]
  ------------------
  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|     66|    } else {
  273|     66|      ret.implementations_agree = true;
  274|     66|    }
  275|     66|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|     66|      return r.error == simdutf::SUCCESS;
  277|     66|    });
  278|     66|    return ret;
  279|     66|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    132|    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|    154|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    154|      return r.error == simdutf::SUCCESS;
  277|    154|    });
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE15count_the_inputENSt3__14spanIS5_Lm18446744073709551615EEE:
  289|     66|  bool count_the_input(FromSpan src) const {
  290|     66|    const auto implementations = get_supported_implementations();
  291|     66|    std::vector<std::size_t> results;
  292|     66|    results.reserve(implementations.size());
  293|       |
  294|    198|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 198, False: 66]
  ------------------
  295|    198|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|    198|      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|    198|        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|    198|      results.push_back(ret);
  304|    198|    }
  305|     66|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|     66|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 66]
  ------------------
  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|     66|    return true;
  321|     66|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE15count_the_inputENSt3__14spanIS5_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|    132|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEb:
  342|     66|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|     66|    length_result ret{};
  344|       |
  345|     66|    const auto implementations = get_supported_implementations();
  346|     66|    std::vector<std::size_t> results;
  347|     66|    results.reserve(implementations.size());
  348|       |
  349|    198|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 198, False: 66]
  ------------------
  350|    198|      const auto len = invoke_lengthcalc(impl, src);
  351|    198|      results.push_back(len);
  352|    198|      ret.length.push_back(len);
  353|    198|    }
  354|       |
  355|     66|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|     66|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 66]
  ------------------
  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|     66|    } else {
  375|     66|      ret.implementations_agree = true;
  376|     66|    }
  377|     66|    return ret;
  378|     66|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSE_NSt3__14spanIS5_Lm18446744073709551615EEE:
  338|    198|                                FromSpan src) const {
  339|    198|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    198|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    132|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|     66|                                  const bool inputisvalid) const {
  383|     66|    conversion_result ret{};
  384|       |
  385|     66|    const auto implementations = get_supported_implementations();
  386|       |
  387|     66|    std::vector<result<ConversionResult>> results;
  388|     66|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|     66|    std::vector<std::vector<ToType>> outputbuffers;
  393|     66|    outputbuffers.reserve(implementations.size());
  394|    264|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 198, False: 66]
  ------------------
  395|    198|      auto impl = implementations[i];
  396|    198|      const ToType canary1{42};
  397|    198|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    198|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    198|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    198|      const auto success = [](const ConversionResult& r) -> bool {
  402|    198|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    198|          return r != 0;
  404|    198|        } else {
  405|    198|          return r.error == simdutf::error_code::SUCCESS;
  406|    198|        }
  407|    198|      }(implret1);
  408|    198|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    198|      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|    198|        const ToType canary2{25};
  414|    198|        const auto outputbuffer_first_run = outputbuffer;
  415|    198|        std::ranges::fill(outputbuffer, canary2);
  416|    198|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    198|                                          src.size(), outputbuffer.data());
  418|       |
  419|    198|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 198]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    198|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 132, False: 66]
  |  Branch (423:29): [True: 57, False: 75]
  ------------------
  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|    198|      }
  441|    198|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 57, False: 141]
  ------------------
  442|    198|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|     66|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 22, False: 44]
  ------------------
  447|     66|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 66, False: 22]
  ------------------
  448|     66|        e.outputhash.clear();
  449|     66|      }
  450|     22|    }
  451|       |
  452|     66|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|     66|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 66]
  ------------------
  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|     66|    } else {
  474|     66|      ret.implementations_agree = true;
  475|     66|    }
  476|     66|    return ret;
  477|     66|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    198|      const auto success = [](const ConversionResult& r) -> bool {
  402|    198|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    198|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    198|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDsmPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    132|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_14clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    147|    +[](std::span<const char> chardata) {                                      \
  555|    147|      const auto c =                                                           \
  556|    147|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    147|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    147|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    147|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    147|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    147|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    147|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    147|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    147|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    147|  constexpr auto _size = _name.size();                                \
  |  | 1183|    147|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    147|  return _nameof; }()
  ------------------
  561|    147|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    147|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    147|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    147|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    147|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    147|  constexpr auto _size = _name.size();                                \
  |  | 1183|    147|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    147|  return _nameof; }()
  ------------------
  562|    147|      c.fuzz(chardata);                                                        \
  563|    147|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_15clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    205|    +[](std::span<const char> chardata) {                                      \
  555|    205|      const auto c =                                                           \
  556|    205|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    205|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    205|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    205|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    205|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    205|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    205|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    205|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    205|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    205|  constexpr auto _size = _name.size();                                \
  |  | 1183|    205|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    205|  return _nameof; }()
  ------------------
  561|    205|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    205|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    205|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    205|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    205|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    205|  constexpr auto _size = _name.size();                                \
  |  | 1183|    205|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    205|  return _nameof; }()
  ------------------
  562|    205|      c.fuzz(chardata);                                                        \
  563|    205|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_16clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     89|    +[](std::span<const char> chardata) {                                      \
  555|     89|      const auto c =                                                           \
  556|     89|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     89|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     89|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     89|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     89|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     89|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     89|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     89|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     89|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     89|  constexpr auto _size = _name.size();                                \
  |  | 1183|     89|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     89|  return _nameof; }()
  ------------------
  561|     89|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     89|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     89|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     89|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     89|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     89|  constexpr auto _size = _name.size();                                \
  |  | 1183|     89|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     89|  return _nameof; }()
  ------------------
  562|     89|      c.fuzz(chardata);                                                        \
  563|     89|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|     89|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|     89|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|     89|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|     89|    static const bool do_print_testcase =
  181|     89|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|     89|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 89]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|     89|    do {
  189|       |      // step 0 - is the input valid?
  190|     89|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|     89|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 89]
  |  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|     89|      const auto [output_length, length_agree] =
  204|     89|          calculate_length(from, inputisvalid);
  205|     89|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 89]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|     89|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 61, False: 28]
  |  Branch (208:28): [True: 0, False: 61]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|     89|      const auto [written, outputs_agree] =
  215|     89|          do_conversion(from, output_length, inputisvalid);
  216|     89|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 89]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|     89|      return;
  221|     89|    } 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|     89|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEE:
  230|     89|  validation_result verify_valid_input(FromSpan src) const {
  231|     89|    validation_result ret{};
  232|       |
  233|     89|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|     89|    const auto implementations = get_supported_implementations();
  235|     89|    std::vector<simdutf::result> results;
  236|     89|    results.reserve(implementations.size());
  237|       |
  238|    267|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 267, False: 89]
  ------------------
  239|    267|      results.push_back(
  240|    267|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    267|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    267|      const bool validation2 =
  245|    267|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    267|                      src.data(), src.size());
  247|    267|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 267]
  ------------------
  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|    267|    }
  258|       |
  259|     89|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|     89|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 89]
  ------------------
  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|     89|    } else {
  273|     89|      ret.implementations_agree = true;
  274|     89|    }
  275|     89|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|     89|      return r.error == simdutf::SUCCESS;
  277|     89|    });
  278|     89|    return ret;
  279|     89|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    178|    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|    145|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    145|      return r.error == simdutf::SUCCESS;
  277|    145|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEb:
  342|     89|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|     89|    length_result ret{};
  344|       |
  345|     89|    const auto implementations = get_supported_implementations();
  346|     89|    std::vector<std::size_t> results;
  347|     89|    results.reserve(implementations.size());
  348|       |
  349|    267|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 267, False: 89]
  ------------------
  350|    267|      const auto len = invoke_lengthcalc(impl, src);
  351|    267|      results.push_back(len);
  352|    267|      ret.length.push_back(len);
  353|    267|    }
  354|       |
  355|     89|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|     89|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 89]
  ------------------
  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|     89|    } else {
  375|     89|      ret.implementations_agree = true;
  376|     89|    }
  377|     89|    return ret;
  378|     89|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSE_NSt3__14spanIS5_Lm18446744073709551615EEE:
  338|    267|                                FromSpan src) const {
  339|    267|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    267|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    178|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|     89|                                  const bool inputisvalid) const {
  383|     89|    conversion_result ret{};
  384|       |
  385|     89|    const auto implementations = get_supported_implementations();
  386|       |
  387|     89|    std::vector<result<ConversionResult>> results;
  388|     89|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|     89|    std::vector<std::vector<ToType>> outputbuffers;
  393|     89|    outputbuffers.reserve(implementations.size());
  394|    356|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 267, False: 89]
  ------------------
  395|    267|      auto impl = implementations[i];
  396|    267|      const ToType canary1{42};
  397|    267|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    267|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    267|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    267|      const auto success = [](const ConversionResult& r) -> bool {
  402|    267|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    267|          return r != 0;
  404|    267|        } else {
  405|    267|          return r.error == simdutf::error_code::SUCCESS;
  406|    267|        }
  407|    267|      }(implret1);
  408|    267|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    267|      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|    267|        const ToType canary2{25};
  414|    267|        const auto outputbuffer_first_run = outputbuffer;
  415|    267|        std::ranges::fill(outputbuffer, canary2);
  416|    267|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    267|                                          src.size(), outputbuffer.data());
  418|       |
  419|    267|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 267]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    267|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 84, False: 183]
  |  Branch (423:29): [True: 72, False: 12]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|     72|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|     72|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 72]
  ------------------
  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|     72|        }
  440|    267|      }
  441|    267|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 72, False: 195]
  ------------------
  442|    267|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|     89|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 61, False: 28]
  ------------------
  447|    183|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 183, False: 61]
  ------------------
  448|    183|        e.outputhash.clear();
  449|    183|      }
  450|     61|    }
  451|       |
  452|     89|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|     89|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 89]
  ------------------
  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|     89|    } else {
  474|     89|      ret.implementations_agree = true;
  475|     89|    }
  476|     89|    return ret;
  477|     89|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    267|      const auto success = [](const ConversionResult& r) -> bool {
  402|    267|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    267|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    267|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKDimPcEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    178|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_17clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    254|    +[](std::span<const char> chardata) {                                      \
  555|    254|      const auto c =                                                           \
  556|    254|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    254|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    254|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    254|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    254|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    254|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    254|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    254|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    254|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    254|  constexpr auto _size = _name.size();                                \
  |  | 1183|    254|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    254|  return _nameof; }()
  ------------------
  561|    254|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    254|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    254|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    254|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    254|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    254|  constexpr auto _size = _name.size();                                \
  |  | 1183|    254|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    254|  return _nameof; }()
  ------------------
  562|    254|      c.fuzz(chardata);                                                        \
  563|    254|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_18clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    248|    +[](std::span<const char> chardata) {                                      \
  555|    248|      const auto c =                                                           \
  556|    248|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    248|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    248|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    248|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    248|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    248|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    248|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    248|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    248|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    248|  constexpr auto _size = _name.size();                                \
  |  | 1183|    248|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    248|  return _nameof; }()
  ------------------
  561|    248|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    248|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    248|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    248|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    248|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    248|  constexpr auto _size = _name.size();                                \
  |  | 1183|    248|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    248|  return _nameof; }()
  ------------------
  562|    248|      c.fuzz(chardata);                                                        \
  563|    248|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_19clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    318|    +[](std::span<const char> chardata) {                                      \
  555|    318|      const auto c =                                                           \
  556|    318|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    318|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    318|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    318|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    318|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    318|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    318|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    318|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    318|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    318|  constexpr auto _size = _name.size();                                \
  |  | 1183|    318|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    318|  return _nameof; }()
  ------------------
  561|    318|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    318|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    318|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    318|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    318|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    318|  constexpr auto _size = _name.size();                                \
  |  | 1183|    318|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    318|  return _nameof; }()
  ------------------
  562|    318|      c.fuzz(chardata);                                                        \
  563|    318|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_20clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    288|    +[](std::span<const char> chardata) {                                      \
  555|    288|      const auto c =                                                           \
  556|    288|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    288|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    288|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    288|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    288|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    288|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    288|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    288|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    288|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    288|  constexpr auto _size = _name.size();                                \
  |  | 1183|    288|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    288|  return _nameof; }()
  ------------------
  561|    288|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    288|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    288|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    288|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    288|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    288|  constexpr auto _size = _name.size();                                \
  |  | 1183|    288|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    288|  return _nameof; }()
  ------------------
  562|    288|      c.fuzz(chardata);                                                        \
  563|    288|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    288|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    288|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    288|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    288|    static const bool do_print_testcase =
  181|    288|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    288|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 288]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    288|    do {
  189|       |      // step 0 - is the input valid?
  190|    288|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    288|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 288]
  |  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|    288|                    From == UtfEncodings::UTF8) {
  198|    288|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 288]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    288|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    288|      const auto [output_length, length_agree] =
  204|    288|          calculate_length(from, inputisvalid);
  205|    288|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 288]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    288|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 187, False: 101]
  |  Branch (208:28): [True: 0, False: 187]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    288|      const auto [written, outputs_agree] =
  215|    288|          do_conversion(from, output_length, inputisvalid);
  216|    288|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 288]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    288|      return;
  221|    288|    } 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|    288|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    288|  validation_result verify_valid_input(FromSpan src) const {
  231|    288|    validation_result ret{};
  232|       |
  233|    288|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    288|    const auto implementations = get_supported_implementations();
  235|    288|    std::vector<simdutf::result> results;
  236|    288|    results.reserve(implementations.size());
  237|       |
  238|    864|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 864, False: 288]
  ------------------
  239|    864|      results.push_back(
  240|    864|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    864|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    864|      const bool validation2 =
  245|    864|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    864|                      src.data(), src.size());
  247|    864|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 864]
  ------------------
  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|    864|    }
  258|       |
  259|    288|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    288|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 288]
  ------------------
  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|    288|    } else {
  273|    288|      ret.implementations_agree = true;
  274|    288|    }
  275|    288|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    288|      return r.error == simdutf::SUCCESS;
  277|    288|    });
  278|    288|    return ret;
  279|    288|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clINS1_6resultESO_EEDaSI_SL_:
  259|    576|    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|    490|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    490|      return r.error == simdutf::SUCCESS;
  277|    490|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    288|  bool count_the_input(FromSpan src) const {
  290|    288|    const auto implementations = get_supported_implementations();
  291|    288|    std::vector<std::size_t> results;
  292|    288|    results.reserve(implementations.size());
  293|       |
  294|    864|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 864, False: 288]
  ------------------
  295|    864|      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|    864|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|    864|        ret = impl->count_utf8(src.data(), src.size());
  302|    864|      }
  303|    864|      results.push_back(ret);
  304|    864|    }
  305|    288|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    288|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 288]
  ------------------
  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|    288|    return true;
  321|    288|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  305|    576|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    288|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    288|    length_result ret{};
  344|       |
  345|    288|    const auto implementations = get_supported_implementations();
  346|    288|    std::vector<std::size_t> results;
  347|    288|    results.reserve(implementations.size());
  348|       |
  349|    864|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 864, False: 288]
  ------------------
  350|    864|      const auto len = invoke_lengthcalc(impl, src);
  351|    864|      results.push_back(len);
  352|    864|      ret.length.push_back(len);
  353|    864|    }
  354|       |
  355|    288|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    288|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 288]
  ------------------
  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|    288|    } else {
  375|    288|      ret.implementations_agree = true;
  376|    288|    }
  377|    288|    return ret;
  378|    288|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    864|                                FromSpan src) const {
  331|    864|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    864|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    576|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    288|                                  const bool inputisvalid) const {
  383|    288|    conversion_result ret{};
  384|       |
  385|    288|    const auto implementations = get_supported_implementations();
  386|       |
  387|    288|    std::vector<result<ConversionResult>> results;
  388|    288|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    288|    std::vector<std::vector<ToType>> outputbuffers;
  393|    288|    outputbuffers.reserve(implementations.size());
  394|  1.15k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 864, False: 288]
  ------------------
  395|    864|      auto impl = implementations[i];
  396|    864|      const ToType canary1{42};
  397|    864|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    864|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    864|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    864|      const auto success = [](const ConversionResult& r) -> bool {
  402|    864|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    864|          return r != 0;
  404|    864|        } else {
  405|    864|          return r.error == simdutf::error_code::SUCCESS;
  406|    864|        }
  407|    864|      }(implret1);
  408|    864|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    864|      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|    864|        const ToType canary2{25};
  414|    864|        const auto outputbuffer_first_run = outputbuffer;
  415|    864|        std::ranges::fill(outputbuffer, canary2);
  416|    864|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    864|                                          src.size(), outputbuffer.data());
  418|       |
  419|    864|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 864]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    864|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 303, False: 561]
  |  Branch (423:29): [True: 276, False: 27]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    276|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    276|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 276]
  ------------------
  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|    276|        }
  440|    864|      }
  441|    864|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 276, False: 588]
  ------------------
  442|    864|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    288|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 187, False: 101]
  ------------------
  447|    561|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 561, False: 187]
  ------------------
  448|    561|        e.outputhash.clear();
  449|    561|      }
  450|    187|    }
  451|       |
  452|    288|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    288|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 288]
  ------------------
  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|    288|    } else {
  474|    288|      ret.implementations_agree = true;
  475|    288|    }
  476|    288|    return ret;
  477|    288|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    864|      const auto success = [](const ConversionResult& r) -> bool {
  402|    864|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    864|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    864|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    576|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_21clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    379|    +[](std::span<const char> chardata) {                                      \
  555|    379|      const auto c =                                                           \
  556|    379|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    379|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    379|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    379|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    379|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    379|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    379|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    379|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    379|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    379|  constexpr auto _size = _name.size();                                \
  |  | 1183|    379|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    379|  return _nameof; }()
  ------------------
  561|    379|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    379|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    379|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    379|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    379|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    379|  constexpr auto _size = _name.size();                                \
  |  | 1183|    379|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    379|  return _nameof; }()
  ------------------
  562|    379|      c.fuzz(chardata);                                                        \
  563|    379|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_22clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    377|    +[](std::span<const char> chardata) {                                      \
  555|    377|      const auto c =                                                           \
  556|    377|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    377|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    377|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    377|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    377|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    377|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    377|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    377|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    377|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    377|  constexpr auto _size = _name.size();                                \
  |  | 1183|    377|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    377|  return _nameof; }()
  ------------------
  561|    377|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    377|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    377|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    377|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    377|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    377|  constexpr auto _size = _name.size();                                \
  |  | 1183|    377|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    377|  return _nameof; }()
  ------------------
  562|    377|      c.fuzz(chardata);                                                        \
  563|    377|    }                                                                          \
conversion.cpp:_ZZ18populate_functionsvENK4$_23clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    379|    +[](std::span<const char> chardata) {                                      \
  555|    379|      const auto c =                                                           \
  556|    379|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    379|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    379|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    379|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    379|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    379|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    379|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    379|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    379|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    379|  constexpr auto _size = _name.size();                                \
  |  | 1183|    379|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    379|  return _nameof; }()
  ------------------
  561|    379|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    379|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    379|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    379|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    379|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    379|  constexpr auto _size = _name.size();                                \
  |  | 1183|    379|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    379|  return _nameof; }()
  ------------------
  562|    379|      c.fuzz(chardata);                                                        \
  563|    379|    }                                                                          \
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: 30, False: 94]
  |  Branch (208:28): [True: 0, False: 30]
  ------------------
  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|    312|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    312|      return r.error == simdutf::SUCCESS;
  277|    312|    });
_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: 282, False: 90]
  |  Branch (423:29): [True: 132, False: 150]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    132|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    132|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 132]
  ------------------
  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|    132|        }
  440|    372|      }
  441|    372|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 132, False: 240]
  ------------------
  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: 30, False: 94]
  ------------------
  447|     90|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 90, False: 30]
  ------------------
  448|     90|        e.outputhash.clear();
  449|     90|      }
  450|     30|    }
  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|    181|    +[](std::span<const char> chardata) {                                      \
  555|    181|      const auto c =                                                           \
  556|    181|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    181|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    181|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    181|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    181|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    181|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    181|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    181|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    181|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    181|  constexpr auto _size = _name.size();                                \
  |  | 1183|    181|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    181|  return _nameof; }()
  ------------------
  561|    181|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    181|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    181|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    181|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    181|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    181|  constexpr auto _size = _name.size();                                \
  |  | 1183|    181|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    181|  return _nameof; }()
  ------------------
  562|    181|      c.fuzz(chardata);                                                        \
  563|    181|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    181|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    181|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    181|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    181|    static const bool do_print_testcase =
  181|    181|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    181|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 181]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    181|    do {
  189|       |      // step 0 - is the input valid?
  190|    181|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    181|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 181]
  |  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|    181|                    From == UtfEncodings::UTF8) {
  198|    181|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 181]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    181|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    181|      const auto [output_length, length_agree] =
  204|    181|          calculate_length(from, inputisvalid);
  205|    181|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 181]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    181|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 94, False: 87]
  |  Branch (208:28): [True: 0, False: 94]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    181|      const auto [written, outputs_agree] =
  215|    181|          do_conversion(from, output_length, inputisvalid);
  216|    181|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 181]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    181|      return;
  221|    181|    } 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|    181|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    181|  validation_result verify_valid_input(FromSpan src) const {
  231|    181|    validation_result ret{};
  232|       |
  233|    181|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    181|    const auto implementations = get_supported_implementations();
  235|    181|    std::vector<simdutf::result> results;
  236|    181|    results.reserve(implementations.size());
  237|       |
  238|    543|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 543, False: 181]
  ------------------
  239|    543|      results.push_back(
  240|    543|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    543|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    543|      const bool validation2 =
  245|    543|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    543|                      src.data(), src.size());
  247|    543|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 543]
  ------------------
  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|    543|    }
  258|       |
  259|    181|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    181|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 181]
  ------------------
  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|    181|    } else {
  273|    181|      ret.implementations_agree = true;
  274|    181|    }
  275|    181|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    181|      return r.error == simdutf::SUCCESS;
  277|    181|    });
  278|    181|    return ret;
  279|    181|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    362|    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|    355|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    355|      return r.error == simdutf::SUCCESS;
  277|    355|    });
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    181|  bool count_the_input(FromSpan src) const {
  290|    181|    const auto implementations = get_supported_implementations();
  291|    181|    std::vector<std::size_t> results;
  292|    181|    results.reserve(implementations.size());
  293|       |
  294|    543|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 543, False: 181]
  ------------------
  295|    543|      std::size_t ret;
  296|    543|      if constexpr (From == UtfEncodings::UTF16BE) {
  297|    543|        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|    543|      results.push_back(ret);
  304|    543|    }
  305|    181|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    181|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 181]
  ------------------
  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|    181|    return true;
  321|    181|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    362|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    181|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    181|    length_result ret{};
  344|       |
  345|    181|    const auto implementations = get_supported_implementations();
  346|    181|    std::vector<std::size_t> results;
  347|    181|    results.reserve(implementations.size());
  348|       |
  349|    543|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 543, False: 181]
  ------------------
  350|    543|      const auto len = invoke_lengthcalc(impl, src);
  351|    543|      results.push_back(len);
  352|    543|      ret.length.push_back(len);
  353|    543|    }
  354|       |
  355|    181|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    181|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 181]
  ------------------
  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|    181|    } else {
  375|    181|      ret.implementations_agree = true;
  376|    181|    }
  377|    181|    return ret;
  378|    181|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    543|                                FromSpan src) const {
  331|    543|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    543|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    362|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    181|                                  const bool inputisvalid) const {
  383|    181|    conversion_result ret{};
  384|       |
  385|    181|    const auto implementations = get_supported_implementations();
  386|       |
  387|    181|    std::vector<result<ConversionResult>> results;
  388|    181|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    181|    std::vector<std::vector<ToType>> outputbuffers;
  393|    181|    outputbuffers.reserve(implementations.size());
  394|    724|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 543, False: 181]
  ------------------
  395|    543|      auto impl = implementations[i];
  396|    543|      const ToType canary1{42};
  397|    543|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    543|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    543|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    543|      const auto success = [](const ConversionResult& r) -> bool {
  402|    543|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    543|          return r != 0;
  404|    543|        } else {
  405|    543|          return r.error == simdutf::error_code::SUCCESS;
  406|    543|        }
  407|    543|      }(implret1);
  408|    543|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    543|      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|    543|        const ToType canary2{25};
  414|    543|        const auto outputbuffer_first_run = outputbuffer;
  415|    543|        std::ranges::fill(outputbuffer, canary2);
  416|    543|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    543|                                          src.size(), outputbuffer.data());
  418|       |
  419|    543|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 543]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    543|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 261, False: 282]
  |  Branch (423:29): [True: 261, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    261|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    261|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 261]
  ------------------
  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|    261|        }
  440|    543|      }
  441|    543|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 261, False: 282]
  ------------------
  442|    543|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    181|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 94, False: 87]
  ------------------
  447|    282|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 282, False: 94]
  ------------------
  448|    282|        e.outputhash.clear();
  449|    282|      }
  450|     94|    }
  451|       |
  452|    181|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    181|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 181]
  ------------------
  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|    181|    } else {
  474|    181|      ret.implementations_agree = true;
  475|    181|    }
  476|    181|    return ret;
  477|    181|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|    543|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|    543|        } else {
  405|    543|          return r.error == simdutf::error_code::SUCCESS;
  406|    543|        }
  407|    543|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings0ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    362|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_26clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    358|    +[](std::span<const char> chardata) {                                      \
  555|    358|      const auto c =                                                           \
  556|    358|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    358|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    358|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    358|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    358|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    358|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    358|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    358|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    358|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    358|  constexpr auto _size = _name.size();                                \
  |  | 1183|    358|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    358|  return _nameof; }()
  ------------------
  561|    358|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    358|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    358|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    358|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    358|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    358|  constexpr auto _size = _name.size();                                \
  |  | 1183|    358|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    358|  return _nameof; }()
  ------------------
  562|    358|      c.fuzz(chardata);                                                        \
  563|    358|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    358|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    358|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    358|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    358|    static const bool do_print_testcase =
  181|    358|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    358|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 358]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    358|    do {
  189|       |      // step 0 - is the input valid?
  190|    358|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    358|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 358]
  |  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|    358|                    From == UtfEncodings::UTF8) {
  198|    358|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 358]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    358|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    358|      const auto [output_length, length_agree] =
  204|    358|          calculate_length(from, inputisvalid);
  205|    358|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 358]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    358|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 147, False: 211]
  |  Branch (208:28): [True: 0, False: 147]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    358|      const auto [written, outputs_agree] =
  215|    358|          do_conversion(from, output_length, inputisvalid);
  216|    358|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 358]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    358|      return;
  221|    358|    } 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|    358|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    358|  validation_result verify_valid_input(FromSpan src) const {
  231|    358|    validation_result ret{};
  232|       |
  233|    358|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    358|    const auto implementations = get_supported_implementations();
  235|    358|    std::vector<simdutf::result> results;
  236|    358|    results.reserve(implementations.size());
  237|       |
  238|  1.07k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.07k, False: 358]
  ------------------
  239|  1.07k|      results.push_back(
  240|  1.07k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.07k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.07k|      const bool validation2 =
  245|  1.07k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.07k|                      src.data(), src.size());
  247|  1.07k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.07k]
  ------------------
  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.07k|    }
  258|       |
  259|    358|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    358|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 358]
  ------------------
  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|    358|    } else {
  273|    358|      ret.implementations_agree = true;
  274|    358|    }
  275|    358|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    358|      return r.error == simdutf::SUCCESS;
  277|    358|    });
  278|    358|    return ret;
  279|    358|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    716|    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|    780|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    780|      return r.error == simdutf::SUCCESS;
  277|    780|    });
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    358|  bool count_the_input(FromSpan src) const {
  290|    358|    const auto implementations = get_supported_implementations();
  291|    358|    std::vector<std::size_t> results;
  292|    358|    results.reserve(implementations.size());
  293|       |
  294|  1.07k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.07k, False: 358]
  ------------------
  295|  1.07k|      std::size_t ret;
  296|  1.07k|      if constexpr (From == UtfEncodings::UTF16BE) {
  297|  1.07k|        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.07k|      results.push_back(ret);
  304|  1.07k|    }
  305|    358|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    358|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 358]
  ------------------
  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|    358|    return true;
  321|    358|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    716|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    358|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    358|    length_result ret{};
  344|       |
  345|    358|    const auto implementations = get_supported_implementations();
  346|    358|    std::vector<std::size_t> results;
  347|    358|    results.reserve(implementations.size());
  348|       |
  349|  1.07k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.07k, False: 358]
  ------------------
  350|  1.07k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.07k|      results.push_back(len);
  352|  1.07k|      ret.length.push_back(len);
  353|  1.07k|    }
  354|       |
  355|    358|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    358|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 358]
  ------------------
  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|    358|    } else {
  375|    358|      ret.implementations_agree = true;
  376|    358|    }
  377|    358|    return ret;
  378|    358|  }
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.07k|                                FromSpan src) const {
  331|  1.07k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.07k|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    716|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    358|                                  const bool inputisvalid) const {
  383|    358|    conversion_result ret{};
  384|       |
  385|    358|    const auto implementations = get_supported_implementations();
  386|       |
  387|    358|    std::vector<result<ConversionResult>> results;
  388|    358|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    358|    std::vector<std::vector<ToType>> outputbuffers;
  393|    358|    outputbuffers.reserve(implementations.size());
  394|  1.43k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.07k, False: 358]
  ------------------
  395|  1.07k|      auto impl = implementations[i];
  396|  1.07k|      const ToType canary1{42};
  397|  1.07k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.07k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.07k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.07k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.07k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.07k|          return r != 0;
  404|  1.07k|        } else {
  405|  1.07k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.07k|        }
  407|  1.07k|      }(implret1);
  408|  1.07k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.07k|      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.07k|        const ToType canary2{25};
  414|  1.07k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.07k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.07k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.07k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.07k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.07k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.07k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 633, False: 441]
  |  Branch (423:29): [True: 633, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    633|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    633|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 633]
  ------------------
  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|    633|        }
  440|  1.07k|      }
  441|  1.07k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 633, False: 441]
  ------------------
  442|  1.07k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    358|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 147, False: 211]
  ------------------
  447|    441|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 441, False: 147]
  ------------------
  448|    441|        e.outputhash.clear();
  449|    441|      }
  450|    147|    }
  451|       |
  452|    358|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    358|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 358]
  ------------------
  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|    358|    } else {
  474|    358|      ret.implementations_agree = true;
  475|    358|    }
  476|    358|    return ret;
  477|    358|  }
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|  1.07k|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|  1.07k|        } else {
  405|  1.07k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.07k|        }
  407|  1.07k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings0ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    716|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_27clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    141|    +[](std::span<const char> chardata) {                                      \
  555|    141|      const auto c =                                                           \
  556|    141|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    141|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    141|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    141|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    141|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    141|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    141|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    141|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    141|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    141|  constexpr auto _size = _name.size();                                \
  |  | 1183|    141|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    141|  return _nameof; }()
  ------------------
  561|    141|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    141|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    141|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    141|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    141|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    141|  constexpr auto _size = _name.size();                                \
  |  | 1183|    141|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    141|  return _nameof; }()
  ------------------
  562|    141|      c.fuzz(chardata);                                                        \
  563|    141|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    141|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    141|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    141|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    141|    static const bool do_print_testcase =
  181|    141|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    141|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 141]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    141|    do {
  189|       |      // step 0 - is the input valid?
  190|    141|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    141|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 141]
  |  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|    141|                    From == UtfEncodings::UTF8) {
  198|    141|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 141]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    141|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    141|      const auto [output_length, length_agree] =
  204|    141|          calculate_length(from, inputisvalid);
  205|    141|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 141]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    141|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 43, False: 98]
  |  Branch (208:28): [True: 0, False: 43]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    141|      const auto [written, outputs_agree] =
  215|    141|          do_conversion(from, output_length, inputisvalid);
  216|    141|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 141]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    141|      return;
  221|    141|    } 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|    141|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS6_Lm18446744073709551615EEE:
  230|    141|  validation_result verify_valid_input(FromSpan src) const {
  231|    141|    validation_result ret{};
  232|       |
  233|    141|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    141|    const auto implementations = get_supported_implementations();
  235|    141|    std::vector<simdutf::result> results;
  236|    141|    results.reserve(implementations.size());
  237|       |
  238|    423|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 423, False: 141]
  ------------------
  239|    423|      results.push_back(
  240|    423|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    423|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    423|      const bool validation2 =
  245|    423|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    423|                      src.data(), src.size());
  247|    423|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 423]
  ------------------
  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|    423|    }
  258|       |
  259|    141|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    141|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 141]
  ------------------
  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|    141|    } else {
  273|    141|      ret.implementations_agree = true;
  274|    141|    }
  275|    141|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    141|      return r.error == simdutf::SUCCESS;
  277|    141|    });
  278|    141|    return ret;
  279|    141|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS6_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS5_S5_EEDaSJ_SM_:
  259|    282|    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|    337|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    337|      return r.error == simdutf::SUCCESS;
  277|    337|    });
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE15count_the_inputENSt3__14spanIS6_Lm18446744073709551615EEE:
  289|    141|  bool count_the_input(FromSpan src) const {
  290|    141|    const auto implementations = get_supported_implementations();
  291|    141|    std::vector<std::size_t> results;
  292|    141|    results.reserve(implementations.size());
  293|       |
  294|    423|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 423, False: 141]
  ------------------
  295|    423|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|    423|      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|    423|        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|    423|      results.push_back(ret);
  304|    423|    }
  305|    141|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    141|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 141]
  ------------------
  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|    141|    return true;
  321|    141|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE15count_the_inputENSt3__14spanIS6_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    282|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE16calculate_lengthENSt3__14spanIS6_Lm18446744073709551615EEEb:
  342|    141|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    141|    length_result ret{};
  344|       |
  345|    141|    const auto implementations = get_supported_implementations();
  346|    141|    std::vector<std::size_t> results;
  347|    141|    results.reserve(implementations.size());
  348|       |
  349|    423|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 423, False: 141]
  ------------------
  350|    423|      const auto len = invoke_lengthcalc(impl, src);
  351|    423|      results.push_back(len);
  352|    423|      ret.length.push_back(len);
  353|    423|    }
  354|       |
  355|    141|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    141|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 141]
  ------------------
  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|    141|    } else {
  375|    141|      ret.implementations_agree = true;
  376|    141|    }
  377|    141|    return ret;
  378|    141|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSF_NSt3__14spanIS6_Lm18446744073709551615EEE:
  338|    423|                                FromSpan src) const {
  339|    423|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    423|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE16calculate_lengthENSt3__14spanIS6_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    282|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    141|                                  const bool inputisvalid) const {
  383|    141|    conversion_result ret{};
  384|       |
  385|    141|    const auto implementations = get_supported_implementations();
  386|       |
  387|    141|    std::vector<result<ConversionResult>> results;
  388|    141|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    141|    std::vector<std::vector<ToType>> outputbuffers;
  393|    141|    outputbuffers.reserve(implementations.size());
  394|    564|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 423, False: 141]
  ------------------
  395|    423|      auto impl = implementations[i];
  396|    423|      const ToType canary1{42};
  397|    423|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    423|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    423|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    423|      const auto success = [](const ConversionResult& r) -> bool {
  402|    423|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    423|          return r != 0;
  404|    423|        } else {
  405|    423|          return r.error == simdutf::error_code::SUCCESS;
  406|    423|        }
  407|    423|      }(implret1);
  408|    423|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    423|      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|    423|        const ToType canary2{25};
  414|    423|        const auto outputbuffer_first_run = outputbuffer;
  415|    423|        std::ranges::fill(outputbuffer, canary2);
  416|    423|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    423|                                          src.size(), outputbuffer.data());
  418|       |
  419|    423|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 423]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    423|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 294, False: 129]
  |  Branch (423:29): [True: 159, False: 135]
  ------------------
  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|    423|      }
  441|    423|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 159, False: 264]
  ------------------
  442|    423|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    141|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 43, False: 98]
  ------------------
  447|    129|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 129, False: 43]
  ------------------
  448|    129|        e.outputhash.clear();
  449|    129|      }
  450|     43|    }
  451|       |
  452|    141|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    141|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 141]
  ------------------
  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|    141|    } else {
  474|    141|      ret.implementations_agree = true;
  475|    141|    }
  476|    141|    return ret;
  477|    141|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS5_E_clESM_:
  401|    423|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|    423|        } else {
  405|    423|          return r.error == simdutf::error_code::SUCCESS;
  406|    423|        }
  407|    423|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings1ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDsmPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS5_ESU_EEDaSN_SQ_:
  452|    282|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_28clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    204|    +[](std::span<const char> chardata) {                                      \
  555|    204|      const auto c =                                                           \
  556|    204|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    204|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    204|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    204|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    204|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    204|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    204|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    204|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    204|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    204|  constexpr auto _size = _name.size();                                \
  |  | 1183|    204|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    204|  return _nameof; }()
  ------------------
  561|    204|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    204|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    204|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    204|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    204|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    204|  constexpr auto _size = _name.size();                                \
  |  | 1183|    204|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    204|  return _nameof; }()
  ------------------
  562|    204|      c.fuzz(chardata);                                                        \
  563|    204|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    204|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    204|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    204|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    204|    static const bool do_print_testcase =
  181|    204|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    204|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 204]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    204|    do {
  189|       |      // step 0 - is the input valid?
  190|    204|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    204|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 204]
  |  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|    204|                    From == UtfEncodings::UTF8) {
  198|    204|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 204]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    204|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    204|      const auto [output_length, length_agree] =
  204|    204|          calculate_length(from, inputisvalid);
  205|    204|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 204]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    204|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 115, False: 89]
  |  Branch (208:28): [True: 0, False: 115]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    204|      const auto [written, outputs_agree] =
  215|    204|          do_conversion(from, output_length, inputisvalid);
  216|    204|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 204]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    204|      return;
  221|    204|    } 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|    204|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    204|  validation_result verify_valid_input(FromSpan src) const {
  231|    204|    validation_result ret{};
  232|       |
  233|    204|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    204|    const auto implementations = get_supported_implementations();
  235|    204|    std::vector<simdutf::result> results;
  236|    204|    results.reserve(implementations.size());
  237|       |
  238|    612|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 612, False: 204]
  ------------------
  239|    612|      results.push_back(
  240|    612|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    612|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    612|      const bool validation2 =
  245|    612|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    612|                      src.data(), src.size());
  247|    612|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 612]
  ------------------
  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|    612|    }
  258|       |
  259|    204|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    204|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 204]
  ------------------
  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|    204|    } else {
  273|    204|      ret.implementations_agree = true;
  274|    204|    }
  275|    204|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    204|      return r.error == simdutf::SUCCESS;
  277|    204|    });
  278|    204|    return ret;
  279|    204|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    408|    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|    382|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    382|      return r.error == simdutf::SUCCESS;
  277|    382|    });
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    204|  bool count_the_input(FromSpan src) const {
  290|    204|    const auto implementations = get_supported_implementations();
  291|    204|    std::vector<std::size_t> results;
  292|    204|    results.reserve(implementations.size());
  293|       |
  294|    612|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 612, False: 204]
  ------------------
  295|    612|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|    612|      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|    612|        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|    612|      results.push_back(ret);
  304|    612|    }
  305|    204|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    204|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 204]
  ------------------
  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|    204|    return true;
  321|    204|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    408|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    204|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    204|    length_result ret{};
  344|       |
  345|    204|    const auto implementations = get_supported_implementations();
  346|    204|    std::vector<std::size_t> results;
  347|    204|    results.reserve(implementations.size());
  348|       |
  349|    612|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 612, False: 204]
  ------------------
  350|    612|      const auto len = invoke_lengthcalc(impl, src);
  351|    612|      results.push_back(len);
  352|    612|      ret.length.push_back(len);
  353|    612|    }
  354|       |
  355|    204|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    204|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 204]
  ------------------
  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|    204|    } else {
  375|    204|      ret.implementations_agree = true;
  376|    204|    }
  377|    204|    return ret;
  378|    204|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    612|                                FromSpan src) const {
  331|    612|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    612|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    408|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    204|                                  const bool inputisvalid) const {
  383|    204|    conversion_result ret{};
  384|       |
  385|    204|    const auto implementations = get_supported_implementations();
  386|       |
  387|    204|    std::vector<result<ConversionResult>> results;
  388|    204|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    204|    std::vector<std::vector<ToType>> outputbuffers;
  393|    204|    outputbuffers.reserve(implementations.size());
  394|    816|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 612, False: 204]
  ------------------
  395|    612|      auto impl = implementations[i];
  396|    612|      const ToType canary1{42};
  397|    612|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    612|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    612|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    612|      const auto success = [](const ConversionResult& r) -> bool {
  402|    612|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    612|          return r != 0;
  404|    612|        } else {
  405|    612|          return r.error == simdutf::error_code::SUCCESS;
  406|    612|        }
  407|    612|      }(implret1);
  408|    612|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    612|      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|    612|        const ToType canary2{25};
  414|    612|        const auto outputbuffer_first_run = outputbuffer;
  415|    612|        std::ranges::fill(outputbuffer, canary2);
  416|    612|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    612|                                          src.size(), outputbuffer.data());
  418|       |
  419|    612|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 612]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    612|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 267, False: 345]
  |  Branch (423:29): [True: 267, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    267|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    267|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 267]
  ------------------
  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|    267|        }
  440|    612|      }
  441|    612|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 267, False: 345]
  ------------------
  442|    612|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    204|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 115, False: 89]
  ------------------
  447|    345|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 345, False: 115]
  ------------------
  448|    345|        e.outputhash.clear();
  449|    345|      }
  450|    115|    }
  451|       |
  452|    204|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    204|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 204]
  ------------------
  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|    204|    } else {
  474|    204|      ret.implementations_agree = true;
  475|    204|    }
  476|    204|    return ret;
  477|    204|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|    612|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|    612|        } else {
  405|    612|          return r.error == simdutf::error_code::SUCCESS;
  406|    612|        }
  407|    612|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings1ELS0_3EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    408|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_29clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    339|    +[](std::span<const char> chardata) {                                      \
  555|    339|      const auto c =                                                           \
  556|    339|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    339|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    339|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    339|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    339|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    339|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    339|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    339|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    339|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    339|  constexpr auto _size = _name.size();                                \
  |  | 1183|    339|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    339|  return _nameof; }()
  ------------------
  561|    339|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    339|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    339|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    339|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    339|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    339|  constexpr auto _size = _name.size();                                \
  |  | 1183|    339|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    339|  return _nameof; }()
  ------------------
  562|    339|      c.fuzz(chardata);                                                        \
  563|    339|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    339|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    339|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    339|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    339|    static const bool do_print_testcase =
  181|    339|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    339|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 339]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    339|    do {
  189|       |      // step 0 - is the input valid?
  190|    339|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    339|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 339]
  |  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|    339|                    From == UtfEncodings::UTF8) {
  198|    339|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 339]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    339|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    339|      const auto [output_length, length_agree] =
  204|    339|          calculate_length(from, inputisvalid);
  205|    339|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 339]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    339|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 141, False: 198]
  |  Branch (208:28): [True: 0, False: 141]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    339|      const auto [written, outputs_agree] =
  215|    339|          do_conversion(from, output_length, inputisvalid);
  216|    339|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 339]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    339|      return;
  221|    339|    } 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|    339|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    339|  validation_result verify_valid_input(FromSpan src) const {
  231|    339|    validation_result ret{};
  232|       |
  233|    339|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    339|    const auto implementations = get_supported_implementations();
  235|    339|    std::vector<simdutf::result> results;
  236|    339|    results.reserve(implementations.size());
  237|       |
  238|  1.01k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.01k, False: 339]
  ------------------
  239|  1.01k|      results.push_back(
  240|  1.01k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.01k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.01k|      const bool validation2 =
  245|  1.01k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.01k|                      src.data(), src.size());
  247|  1.01k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.01k]
  ------------------
  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.01k|    }
  258|       |
  259|    339|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    339|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 339]
  ------------------
  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|    339|    } else {
  273|    339|      ret.implementations_agree = true;
  274|    339|    }
  275|    339|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    339|      return r.error == simdutf::SUCCESS;
  277|    339|    });
  278|    339|    return ret;
  279|    339|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    678|    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|    735|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    735|      return r.error == simdutf::SUCCESS;
  277|    735|    });
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    339|  bool count_the_input(FromSpan src) const {
  290|    339|    const auto implementations = get_supported_implementations();
  291|    339|    std::vector<std::size_t> results;
  292|    339|    results.reserve(implementations.size());
  293|       |
  294|  1.01k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.01k, False: 339]
  ------------------
  295|  1.01k|      std::size_t ret;
  296|       |      if constexpr (From == UtfEncodings::UTF16BE) {
  297|       |        ret = impl->count_utf16be(src.data(), src.size());
  298|  1.01k|      } else if constexpr (From == UtfEncodings::UTF16LE) {
  299|  1.01k|        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.01k|      results.push_back(ret);
  304|  1.01k|    }
  305|    339|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    339|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 339]
  ------------------
  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|    339|    return true;
  321|    339|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    678|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    339|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    339|    length_result ret{};
  344|       |
  345|    339|    const auto implementations = get_supported_implementations();
  346|    339|    std::vector<std::size_t> results;
  347|    339|    results.reserve(implementations.size());
  348|       |
  349|  1.01k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.01k, False: 339]
  ------------------
  350|  1.01k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.01k|      results.push_back(len);
  352|  1.01k|      ret.length.push_back(len);
  353|  1.01k|    }
  354|       |
  355|    339|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    339|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 339]
  ------------------
  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|    339|    } else {
  375|    339|      ret.implementations_agree = true;
  376|    339|    }
  377|    339|    return ret;
  378|    339|  }
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.01k|                                FromSpan src) const {
  331|  1.01k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.01k|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    678|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    339|                                  const bool inputisvalid) const {
  383|    339|    conversion_result ret{};
  384|       |
  385|    339|    const auto implementations = get_supported_implementations();
  386|       |
  387|    339|    std::vector<result<ConversionResult>> results;
  388|    339|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    339|    std::vector<std::vector<ToType>> outputbuffers;
  393|    339|    outputbuffers.reserve(implementations.size());
  394|  1.35k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.01k, False: 339]
  ------------------
  395|  1.01k|      auto impl = implementations[i];
  396|  1.01k|      const ToType canary1{42};
  397|  1.01k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.01k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.01k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.01k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.01k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.01k|          return r != 0;
  404|  1.01k|        } else {
  405|  1.01k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.01k|        }
  407|  1.01k|      }(implret1);
  408|  1.01k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.01k|      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.01k|        const ToType canary2{25};
  414|  1.01k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.01k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.01k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.01k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.01k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.01k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.01k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 594, False: 423]
  |  Branch (423:29): [True: 594, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    594|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    594|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 594]
  ------------------
  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|    594|        }
  440|  1.01k|      }
  441|  1.01k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 594, False: 423]
  ------------------
  442|  1.01k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    339|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 141, False: 198]
  ------------------
  447|    423|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 423, False: 141]
  ------------------
  448|    423|        e.outputhash.clear();
  449|    423|      }
  450|    141|    }
  451|       |
  452|    339|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    339|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 339]
  ------------------
  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|    339|    } else {
  474|    339|      ret.implementations_agree = true;
  475|    339|    }
  476|    339|    return ret;
  477|    339|  }
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|  1.01k|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|  1.01k|        } else {
  405|  1.01k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.01k|        }
  407|  1.01k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings1ELS0_2EMN7simdutf14implementationEKDoFmPKDsmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    678|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_30clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    204|    +[](std::span<const char> chardata) {                                      \
  555|    204|      const auto c =                                                           \
  556|    204|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    204|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    204|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    204|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    204|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    204|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    204|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    204|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    204|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    204|  constexpr auto _size = _name.size();                                \
  |  | 1183|    204|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    204|  return _nameof; }()
  ------------------
  561|    204|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    204|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    204|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    204|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    204|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    204|  constexpr auto _size = _name.size();                                \
  |  | 1183|    204|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    204|  return _nameof; }()
  ------------------
  562|    204|      c.fuzz(chardata);                                                        \
  563|    204|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    204|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    204|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    204|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    204|    static const bool do_print_testcase =
  181|    204|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    204|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 204]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    204|    do {
  189|       |      // step 0 - is the input valid?
  190|    204|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    204|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 204]
  |  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|    204|      const auto [output_length, length_agree] =
  204|    204|          calculate_length(from, inputisvalid);
  205|    204|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 204]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    204|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 174, False: 30]
  |  Branch (208:28): [True: 0, False: 174]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    204|      const auto [written, outputs_agree] =
  215|    204|          do_conversion(from, output_length, inputisvalid);
  216|    204|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 204]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    204|      return;
  221|    204|    } 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|    204|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS6_Lm18446744073709551615EEE:
  230|    204|  validation_result verify_valid_input(FromSpan src) const {
  231|    204|    validation_result ret{};
  232|       |
  233|    204|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    204|    const auto implementations = get_supported_implementations();
  235|    204|    std::vector<simdutf::result> results;
  236|    204|    results.reserve(implementations.size());
  237|       |
  238|    612|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 612, False: 204]
  ------------------
  239|    612|      results.push_back(
  240|    612|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    612|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    612|      const bool validation2 =
  245|    612|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    612|                      src.data(), src.size());
  247|    612|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 612]
  ------------------
  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|    612|    }
  258|       |
  259|    204|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    204|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 204]
  ------------------
  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|    204|    } else {
  273|    204|      ret.implementations_agree = true;
  274|    204|    }
  275|    204|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    204|      return r.error == simdutf::SUCCESS;
  277|    204|    });
  278|    204|    return ret;
  279|    204|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS6_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS5_S5_EEDaSJ_SM_:
  259|    408|    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|    264|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    264|      return r.error == simdutf::SUCCESS;
  277|    264|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE16calculate_lengthENSt3__14spanIS6_Lm18446744073709551615EEEb:
  342|    204|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    204|    length_result ret{};
  344|       |
  345|    204|    const auto implementations = get_supported_implementations();
  346|    204|    std::vector<std::size_t> results;
  347|    204|    results.reserve(implementations.size());
  348|       |
  349|    612|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 612, False: 204]
  ------------------
  350|    612|      const auto len = invoke_lengthcalc(impl, src);
  351|    612|      results.push_back(len);
  352|    612|      ret.length.push_back(len);
  353|    612|    }
  354|       |
  355|    204|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    204|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 204]
  ------------------
  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|    204|    } else {
  375|    204|      ret.implementations_agree = true;
  376|    204|    }
  377|    204|    return ret;
  378|    204|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSF_NSt3__14spanIS6_Lm18446744073709551615EEE:
  338|    612|                                FromSpan src) const {
  339|    612|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    612|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE16calculate_lengthENSt3__14spanIS6_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    408|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    204|                                  const bool inputisvalid) const {
  383|    204|    conversion_result ret{};
  384|       |
  385|    204|    const auto implementations = get_supported_implementations();
  386|       |
  387|    204|    std::vector<result<ConversionResult>> results;
  388|    204|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    204|    std::vector<std::vector<ToType>> outputbuffers;
  393|    204|    outputbuffers.reserve(implementations.size());
  394|    816|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 612, False: 204]
  ------------------
  395|    612|      auto impl = implementations[i];
  396|    612|      const ToType canary1{42};
  397|    612|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    612|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    612|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    612|      const auto success = [](const ConversionResult& r) -> bool {
  402|    612|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    612|          return r != 0;
  404|    612|        } else {
  405|    612|          return r.error == simdutf::error_code::SUCCESS;
  406|    612|        }
  407|    612|      }(implret1);
  408|    612|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    612|      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|    612|        const ToType canary2{25};
  414|    612|        const auto outputbuffer_first_run = outputbuffer;
  415|    612|        std::ranges::fill(outputbuffer, canary2);
  416|    612|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    612|                                          src.size(), outputbuffer.data());
  418|       |
  419|    612|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 612]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    612|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 90, False: 522]
  |  Branch (423:29): [True: 66, False: 24]
  ------------------
  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|    612|      }
  441|    612|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 66, False: 546]
  ------------------
  442|    612|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    204|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 174, False: 30]
  ------------------
  447|    522|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 522, False: 174]
  ------------------
  448|    522|        e.outputhash.clear();
  449|    522|      }
  450|    174|    }
  451|       |
  452|    204|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    204|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 204]
  ------------------
  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|    204|    } else {
  474|    204|      ret.implementations_agree = true;
  475|    204|    }
  476|    204|    return ret;
  477|    204|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS5_E_clESM_:
  401|    612|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|    612|        } else {
  405|    612|          return r.error == simdutf::error_code::SUCCESS;
  406|    612|        }
  407|    612|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_4EMN7simdutf14implementationEKDoFmmEMS2_KDoFNS1_6resultEPKDimPcEE13do_conversionENSt3__14spanIS6_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS5_ESU_EEDaSN_SQ_:
  452|    408|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_31clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    296|    +[](std::span<const char> chardata) {                                      \
  555|    296|      const auto c =                                                           \
  556|    296|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    296|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    296|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    296|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    296|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    296|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    296|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    296|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    296|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    296|  constexpr auto _size = _name.size();                                \
  |  | 1183|    296|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    296|  return _nameof; }()
  ------------------
  561|    296|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    296|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    296|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    296|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    296|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    296|  constexpr auto _size = _name.size();                                \
  |  | 1183|    296|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    296|  return _nameof; }()
  ------------------
  562|    296|      c.fuzz(chardata);                                                        \
  563|    296|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    296|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    296|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    296|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    296|    static const bool do_print_testcase =
  181|    296|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    296|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 296]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    296|    do {
  189|       |      // step 0 - is the input valid?
  190|    296|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    296|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 296]
  |  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|    296|      const auto [output_length, length_agree] =
  204|    296|          calculate_length(from, inputisvalid);
  205|    296|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 296]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    296|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 207, False: 89]
  |  Branch (208:28): [True: 0, False: 207]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    296|      const auto [written, outputs_agree] =
  215|    296|          do_conversion(from, output_length, inputisvalid);
  216|    296|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 296]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    296|      return;
  221|    296|    } 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|    296|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    296|  validation_result verify_valid_input(FromSpan src) const {
  231|    296|    validation_result ret{};
  232|       |
  233|    296|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    296|    const auto implementations = get_supported_implementations();
  235|    296|    std::vector<simdutf::result> results;
  236|    296|    results.reserve(implementations.size());
  237|       |
  238|    888|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 888, False: 296]
  ------------------
  239|    888|      results.push_back(
  240|    888|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    888|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    888|      const bool validation2 =
  245|    888|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    888|                      src.data(), src.size());
  247|    888|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 888]
  ------------------
  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|    888|    }
  258|       |
  259|    296|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    296|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 296]
  ------------------
  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|    296|    } else {
  273|    296|      ret.implementations_agree = true;
  274|    296|    }
  275|    296|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    296|      return r.error == simdutf::SUCCESS;
  277|    296|    });
  278|    296|    return ret;
  279|    296|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    592|    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|    474|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    474|      return r.error == simdutf::SUCCESS;
  277|    474|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    296|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    296|    length_result ret{};
  344|       |
  345|    296|    const auto implementations = get_supported_implementations();
  346|    296|    std::vector<std::size_t> results;
  347|    296|    results.reserve(implementations.size());
  348|       |
  349|    888|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 888, False: 296]
  ------------------
  350|    888|      const auto len = invoke_lengthcalc(impl, src);
  351|    888|      results.push_back(len);
  352|    888|      ret.length.push_back(len);
  353|    888|    }
  354|       |
  355|    296|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    296|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 296]
  ------------------
  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|    296|    } else {
  375|    296|      ret.implementations_agree = true;
  376|    296|    }
  377|    296|    return ret;
  378|    296|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    888|                                FromSpan src) const {
  331|    888|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    888|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    592|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    296|                                  const bool inputisvalid) const {
  383|    296|    conversion_result ret{};
  384|       |
  385|    296|    const auto implementations = get_supported_implementations();
  386|       |
  387|    296|    std::vector<result<ConversionResult>> results;
  388|    296|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    296|    std::vector<std::vector<ToType>> outputbuffers;
  393|    296|    outputbuffers.reserve(implementations.size());
  394|  1.18k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 888, False: 296]
  ------------------
  395|    888|      auto impl = implementations[i];
  396|    888|      const ToType canary1{42};
  397|    888|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    888|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    888|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    888|      const auto success = [](const ConversionResult& r) -> bool {
  402|    888|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    888|          return r != 0;
  404|    888|        } else {
  405|    888|          return r.error == simdutf::error_code::SUCCESS;
  406|    888|        }
  407|    888|      }(implret1);
  408|    888|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    888|      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|    888|        const ToType canary2{25};
  414|    888|        const auto outputbuffer_first_run = outputbuffer;
  415|    888|        std::ranges::fill(outputbuffer, canary2);
  416|    888|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    888|                                          src.size(), outputbuffer.data());
  418|       |
  419|    888|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 888]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    888|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 267, False: 621]
  |  Branch (423:29): [True: 267, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    267|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    267|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 267]
  ------------------
  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|    267|        }
  440|    888|      }
  441|    888|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 267, False: 621]
  ------------------
  442|    888|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    296|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 207, False: 89]
  ------------------
  447|    621|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 621, False: 207]
  ------------------
  448|    621|        e.outputhash.clear();
  449|    621|      }
  450|    207|    }
  451|       |
  452|    296|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    296|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 296]
  ------------------
  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|    296|    } else {
  474|    296|      ret.implementations_agree = true;
  475|    296|    }
  476|    296|    return ret;
  477|    296|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|    888|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|    888|        } else {
  405|    888|          return r.error == simdutf::error_code::SUCCESS;
  406|    888|        }
  407|    888|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_0EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    592|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_32clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    329|    +[](std::span<const char> chardata) {                                      \
  555|    329|      const auto c =                                                           \
  556|    329|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    329|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    329|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    329|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    329|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    329|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    329|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    329|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    329|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    329|  constexpr auto _size = _name.size();                                \
  |  | 1183|    329|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    329|  return _nameof; }()
  ------------------
  561|    329|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    329|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    329|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    329|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    329|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    329|  constexpr auto _size = _name.size();                                \
  |  | 1183|    329|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    329|  return _nameof; }()
  ------------------
  562|    329|      c.fuzz(chardata);                                                        \
  563|    329|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    329|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    329|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    329|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    329|    static const bool do_print_testcase =
  181|    329|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    329|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 329]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    329|    do {
  189|       |      // step 0 - is the input valid?
  190|    329|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    329|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 329]
  |  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|    329|      const auto [output_length, length_agree] =
  204|    329|          calculate_length(from, inputisvalid);
  205|    329|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 329]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    329|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 241, False: 88]
  |  Branch (208:28): [True: 0, False: 241]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    329|      const auto [written, outputs_agree] =
  215|    329|          do_conversion(from, output_length, inputisvalid);
  216|    329|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 329]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    329|      return;
  221|    329|    } 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|    329|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    329|  validation_result verify_valid_input(FromSpan src) const {
  231|    329|    validation_result ret{};
  232|       |
  233|    329|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    329|    const auto implementations = get_supported_implementations();
  235|    329|    std::vector<simdutf::result> results;
  236|    329|    results.reserve(implementations.size());
  237|       |
  238|    987|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 987, False: 329]
  ------------------
  239|    987|      results.push_back(
  240|    987|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    987|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    987|      const bool validation2 =
  245|    987|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    987|                      src.data(), src.size());
  247|    987|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 987]
  ------------------
  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|    987|    }
  258|       |
  259|    329|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    329|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 329]
  ------------------
  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|    329|    } else {
  273|    329|      ret.implementations_agree = true;
  274|    329|    }
  275|    329|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    329|      return r.error == simdutf::SUCCESS;
  277|    329|    });
  278|    329|    return ret;
  279|    329|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    658|    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|    505|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    505|      return r.error == simdutf::SUCCESS;
  277|    505|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    329|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    329|    length_result ret{};
  344|       |
  345|    329|    const auto implementations = get_supported_implementations();
  346|    329|    std::vector<std::size_t> results;
  347|    329|    results.reserve(implementations.size());
  348|       |
  349|    987|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 987, False: 329]
  ------------------
  350|    987|      const auto len = invoke_lengthcalc(impl, src);
  351|    987|      results.push_back(len);
  352|    987|      ret.length.push_back(len);
  353|    987|    }
  354|       |
  355|    329|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    329|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 329]
  ------------------
  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|    329|    } else {
  375|    329|      ret.implementations_agree = true;
  376|    329|    }
  377|    329|    return ret;
  378|    329|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    987|                                FromSpan src) const {
  331|    987|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    987|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    658|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    329|                                  const bool inputisvalid) const {
  383|    329|    conversion_result ret{};
  384|       |
  385|    329|    const auto implementations = get_supported_implementations();
  386|       |
  387|    329|    std::vector<result<ConversionResult>> results;
  388|    329|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    329|    std::vector<std::vector<ToType>> outputbuffers;
  393|    329|    outputbuffers.reserve(implementations.size());
  394|  1.31k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 987, False: 329]
  ------------------
  395|    987|      auto impl = implementations[i];
  396|    987|      const ToType canary1{42};
  397|    987|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    987|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    987|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    987|      const auto success = [](const ConversionResult& r) -> bool {
  402|    987|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    987|          return r != 0;
  404|    987|        } else {
  405|    987|          return r.error == simdutf::error_code::SUCCESS;
  406|    987|        }
  407|    987|      }(implret1);
  408|    987|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    987|      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|    987|        const ToType canary2{25};
  414|    987|        const auto outputbuffer_first_run = outputbuffer;
  415|    987|        std::ranges::fill(outputbuffer, canary2);
  416|    987|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    987|                                          src.size(), outputbuffer.data());
  418|       |
  419|    987|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 987]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    987|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 264, False: 723]
  |  Branch (423:29): [True: 264, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    264|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    264|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 264]
  ------------------
  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|    264|        }
  440|    987|      }
  441|    987|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 264, False: 723]
  ------------------
  442|    987|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    329|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 241, False: 88]
  ------------------
  447|    723|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 723, False: 241]
  ------------------
  448|    723|        e.outputhash.clear();
  449|    723|      }
  450|    241|    }
  451|       |
  452|    329|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    329|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 329]
  ------------------
  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|    329|    } else {
  474|    329|      ret.implementations_agree = true;
  475|    329|    }
  476|    329|    return ret;
  477|    329|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|    987|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|    987|        } else {
  405|    987|          return r.error == simdutf::error_code::SUCCESS;
  406|    987|        }
  407|    987|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_1EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    658|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_33clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    446|    +[](std::span<const char> chardata) {                                      \
  555|    446|      const auto c =                                                           \
  556|    446|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    446|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    446|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    446|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    446|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    446|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    446|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    446|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    446|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    446|  constexpr auto _size = _name.size();                                \
  |  | 1183|    446|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    446|  return _nameof; }()
  ------------------
  561|    446|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    446|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    446|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    446|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    446|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    446|  constexpr auto _size = _name.size();                                \
  |  | 1183|    446|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    446|  return _nameof; }()
  ------------------
  562|    446|      c.fuzz(chardata);                                                        \
  563|    446|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE4fuzzENSt3__14spanIKcLm18446744073709551615EEE:
  175|    446|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    446|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    446|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    446|    static const bool do_print_testcase =
  181|    446|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    446|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 446]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    446|    do {
  189|       |      // step 0 - is the input valid?
  190|    446|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    446|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 446]
  |  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|    446|      const auto [output_length, length_agree] =
  204|    446|          calculate_length(from, inputisvalid);
  205|    446|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 446]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    446|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 335, False: 111]
  |  Branch (208:28): [True: 0, False: 335]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    446|      const auto [written, outputs_agree] =
  215|    446|          do_conversion(from, output_length, inputisvalid);
  216|    446|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 446]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    446|      return;
  221|    446|    } 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|    446|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    446|  validation_result verify_valid_input(FromSpan src) const {
  231|    446|    validation_result ret{};
  232|       |
  233|    446|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    446|    const auto implementations = get_supported_implementations();
  235|    446|    std::vector<simdutf::result> results;
  236|    446|    results.reserve(implementations.size());
  237|       |
  238|  1.33k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.33k, False: 446]
  ------------------
  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|    446|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    446|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 446]
  ------------------
  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|    446|    } else {
  273|    446|      ret.implementations_agree = true;
  274|    446|    }
  275|    446|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    446|      return r.error == simdutf::SUCCESS;
  277|    446|    });
  278|    446|    return ret;
  279|    446|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    892|    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|    668|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    668|      return r.error == simdutf::SUCCESS;
  277|    668|    });
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    446|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    446|    length_result ret{};
  344|       |
  345|    446|    const auto implementations = get_supported_implementations();
  346|    446|    std::vector<std::size_t> results;
  347|    446|    results.reserve(implementations.size());
  348|       |
  349|  1.33k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.33k, False: 446]
  ------------------
  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|    446|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    446|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 446]
  ------------------
  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|    446|    } else {
  375|    446|      ret.implementations_agree = true;
  376|    446|    }
  377|    446|    return ret;
  378|    446|  }
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.33k|                                FromSpan src) const {
  331|  1.33k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.33k|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    892|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    446|                                  const bool inputisvalid) const {
  383|    446|    conversion_result ret{};
  384|       |
  385|    446|    const auto implementations = get_supported_implementations();
  386|       |
  387|    446|    std::vector<result<ConversionResult>> results;
  388|    446|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    446|    std::vector<std::vector<ToType>> outputbuffers;
  393|    446|    outputbuffers.reserve(implementations.size());
  394|  1.78k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.33k, False: 446]
  ------------------
  395|  1.33k|      auto impl = implementations[i];
  396|  1.33k|      const ToType canary1{42};
  397|  1.33k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.33k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.33k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.33k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.33k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.33k|          return r != 0;
  404|  1.33k|        } else {
  405|  1.33k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.33k|        }
  407|  1.33k|      }(implret1);
  408|  1.33k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.33k|      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.33k|        const ToType canary2{25};
  414|  1.33k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.33k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.33k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.33k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.33k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.33k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.33k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 333, False: 1.00k]
  |  Branch (423:29): [True: 333, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    333|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    333|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 333]
  ------------------
  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|    333|        }
  440|  1.33k|      }
  441|  1.33k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 333, False: 1.00k]
  ------------------
  442|  1.33k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    446|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 335, False: 111]
  ------------------
  447|  1.00k|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 1.00k, False: 335]
  ------------------
  448|  1.00k|        e.outputhash.clear();
  449|  1.00k|      }
  450|    335|    }
  451|       |
  452|    446|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    446|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 446]
  ------------------
  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|    446|    } else {
  474|    446|      ret.implementations_agree = true;
  475|    446|    }
  476|    446|    return ret;
  477|    446|  }
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|  1.33k|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|  1.33k|        } else {
  405|  1.33k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.33k|        }
  407|  1.33k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings3ELS0_2EMN7simdutf14implementationEKDoFmPKDimEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    892|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_34clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    250|    +[](std::span<const char> chardata) {                                      \
  555|    250|      const auto c =                                                           \
  556|    250|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    250|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    250|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    250|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    250|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    250|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    250|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    250|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    250|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    250|  constexpr auto _size = _name.size();                                \
  |  | 1183|    250|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    250|  return _nameof; }()
  ------------------
  561|    250|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    250|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    250|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    250|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    250|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    250|  constexpr auto _size = _name.size();                                \
  |  | 1183|    250|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    250|  return _nameof; }()
  ------------------
  562|    250|      c.fuzz(chardata);                                                        \
  563|    250|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    250|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    250|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    250|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    250|    static const bool do_print_testcase =
  181|    250|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    250|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 250]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    250|    do {
  189|       |      // step 0 - is the input valid?
  190|    250|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    250|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 250]
  |  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|    250|                    From == UtfEncodings::UTF8) {
  198|    250|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 250]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    250|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    250|      const auto [output_length, length_agree] =
  204|    250|          calculate_length(from, inputisvalid);
  205|    250|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 250]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    250|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 131, False: 119]
  |  Branch (208:28): [True: 0, False: 131]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    250|      const auto [written, outputs_agree] =
  215|    250|          do_conversion(from, output_length, inputisvalid);
  216|    250|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 250]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    250|      return;
  221|    250|    } 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|    250|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    250|  validation_result verify_valid_input(FromSpan src) const {
  231|    250|    validation_result ret{};
  232|       |
  233|    250|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    250|    const auto implementations = get_supported_implementations();
  235|    250|    std::vector<simdutf::result> results;
  236|    250|    results.reserve(implementations.size());
  237|       |
  238|    750|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 750, False: 250]
  ------------------
  239|    750|      results.push_back(
  240|    750|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    750|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    750|      const bool validation2 =
  245|    750|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    750|                      src.data(), src.size());
  247|    750|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 750]
  ------------------
  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|    750|    }
  258|       |
  259|    250|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    250|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 250]
  ------------------
  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|    250|    } else {
  273|    250|      ret.implementations_agree = true;
  274|    250|    }
  275|    250|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    250|      return r.error == simdutf::SUCCESS;
  277|    250|    });
  278|    250|    return ret;
  279|    250|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    500|    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|    488|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    488|      return r.error == simdutf::SUCCESS;
  277|    488|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    250|  bool count_the_input(FromSpan src) const {
  290|    250|    const auto implementations = get_supported_implementations();
  291|    250|    std::vector<std::size_t> results;
  292|    250|    results.reserve(implementations.size());
  293|       |
  294|    750|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 750, False: 250]
  ------------------
  295|    750|      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|    750|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|    750|        ret = impl->count_utf8(src.data(), src.size());
  302|    750|      }
  303|    750|      results.push_back(ret);
  304|    750|    }
  305|    250|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    250|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 250]
  ------------------
  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|    250|    return true;
  321|    250|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    500|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    250|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    250|    length_result ret{};
  344|       |
  345|    250|    const auto implementations = get_supported_implementations();
  346|    250|    std::vector<std::size_t> results;
  347|    250|    results.reserve(implementations.size());
  348|       |
  349|    750|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 750, False: 250]
  ------------------
  350|    750|      const auto len = invoke_lengthcalc(impl, src);
  351|    750|      results.push_back(len);
  352|    750|      ret.length.push_back(len);
  353|    750|    }
  354|       |
  355|    250|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    250|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 250]
  ------------------
  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|    250|    } else {
  375|    250|      ret.implementations_agree = true;
  376|    250|    }
  377|    250|    return ret;
  378|    250|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    750|                                FromSpan src) const {
  331|    750|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    750|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    500|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    250|                                  const bool inputisvalid) const {
  383|    250|    conversion_result ret{};
  384|       |
  385|    250|    const auto implementations = get_supported_implementations();
  386|       |
  387|    250|    std::vector<result<ConversionResult>> results;
  388|    250|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    250|    std::vector<std::vector<ToType>> outputbuffers;
  393|    250|    outputbuffers.reserve(implementations.size());
  394|  1.00k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 750, False: 250]
  ------------------
  395|    750|      auto impl = implementations[i];
  396|    750|      const ToType canary1{42};
  397|    750|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    750|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    750|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    750|      const auto success = [](const ConversionResult& r) -> bool {
  402|    750|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    750|          return r != 0;
  404|    750|        } else {
  405|    750|          return r.error == simdutf::error_code::SUCCESS;
  406|    750|        }
  407|    750|      }(implret1);
  408|    750|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    750|      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|    750|        const ToType canary2{25};
  414|    750|        const auto outputbuffer_first_run = outputbuffer;
  415|    750|        std::ranges::fill(outputbuffer, canary2);
  416|    750|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    750|                                          src.size(), outputbuffer.data());
  418|       |
  419|    750|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 750]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    750|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 357, False: 393]
  |  Branch (423:29): [True: 345, False: 12]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    345|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    345|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 345]
  ------------------
  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|    345|        }
  440|    750|      }
  441|    750|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 345, False: 405]
  ------------------
  442|    750|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    250|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 131, False: 119]
  ------------------
  447|    393|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 393, False: 131]
  ------------------
  448|    393|        e.outputhash.clear();
  449|    393|      }
  450|    131|    }
  451|       |
  452|    250|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    250|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 250]
  ------------------
  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|    250|    } else {
  474|    250|      ret.implementations_agree = true;
  475|    250|    }
  476|    250|    return ret;
  477|    250|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|    750|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|    750|        } else {
  405|    750|          return r.error == simdutf::error_code::SUCCESS;
  406|    750|        }
  407|    750|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_4EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    500|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_35clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    379|    +[](std::span<const char> chardata) {                                      \
  555|    379|      const auto c =                                                           \
  556|    379|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    379|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    379|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    379|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    379|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    379|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    379|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    379|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    379|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    379|  constexpr auto _size = _name.size();                                \
  |  | 1183|    379|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    379|  return _nameof; }()
  ------------------
  561|    379|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    379|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    379|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    379|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    379|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    379|  constexpr auto _size = _name.size();                                \
  |  | 1183|    379|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    379|  return _nameof; }()
  ------------------
  562|    379|      c.fuzz(chardata);                                                        \
  563|    379|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    379|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    379|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    379|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    379|    static const bool do_print_testcase =
  181|    379|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    379|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 379]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    379|    do {
  189|       |      // step 0 - is the input valid?
  190|    379|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    379|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 379]
  |  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|    379|                    From == UtfEncodings::UTF8) {
  198|    379|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 379]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    379|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    379|      const auto [output_length, length_agree] =
  204|    379|          calculate_length(from, inputisvalid);
  205|    379|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 379]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    379|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 210, False: 169]
  |  Branch (208:28): [True: 0, False: 210]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    379|      const auto [written, outputs_agree] =
  215|    379|          do_conversion(from, output_length, inputisvalid);
  216|    379|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 379]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    379|      return;
  221|    379|    } 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|    379|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    379|  validation_result verify_valid_input(FromSpan src) const {
  231|    379|    validation_result ret{};
  232|       |
  233|    379|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    379|    const auto implementations = get_supported_implementations();
  235|    379|    std::vector<simdutf::result> results;
  236|    379|    results.reserve(implementations.size());
  237|       |
  238|  1.13k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.13k, False: 379]
  ------------------
  239|  1.13k|      results.push_back(
  240|  1.13k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.13k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.13k|      const bool validation2 =
  245|  1.13k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.13k|                      src.data(), src.size());
  247|  1.13k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.13k]
  ------------------
  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.13k|    }
  258|       |
  259|    379|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    379|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 379]
  ------------------
  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|    379|    } else {
  273|    379|      ret.implementations_agree = true;
  274|    379|    }
  275|    379|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    379|      return r.error == simdutf::SUCCESS;
  277|    379|    });
  278|    379|    return ret;
  279|    379|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    758|    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|    717|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    717|      return r.error == simdutf::SUCCESS;
  277|    717|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    379|  bool count_the_input(FromSpan src) const {
  290|    379|    const auto implementations = get_supported_implementations();
  291|    379|    std::vector<std::size_t> results;
  292|    379|    results.reserve(implementations.size());
  293|       |
  294|  1.13k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.13k, False: 379]
  ------------------
  295|  1.13k|      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.13k|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|  1.13k|        ret = impl->count_utf8(src.data(), src.size());
  302|  1.13k|      }
  303|  1.13k|      results.push_back(ret);
  304|  1.13k|    }
  305|    379|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    379|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 379]
  ------------------
  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|    379|    return true;
  321|    379|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    758|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    379|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    379|    length_result ret{};
  344|       |
  345|    379|    const auto implementations = get_supported_implementations();
  346|    379|    std::vector<std::size_t> results;
  347|    379|    results.reserve(implementations.size());
  348|       |
  349|  1.13k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.13k, False: 379]
  ------------------
  350|  1.13k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.13k|      results.push_back(len);
  352|  1.13k|      ret.length.push_back(len);
  353|  1.13k|    }
  354|       |
  355|    379|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    379|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 379]
  ------------------
  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|    379|    } else {
  375|    379|      ret.implementations_agree = true;
  376|    379|    }
  377|    379|    return ret;
  378|    379|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.13k|                                FromSpan src) const {
  331|  1.13k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.13k|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    758|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    379|                                  const bool inputisvalid) const {
  383|    379|    conversion_result ret{};
  384|       |
  385|    379|    const auto implementations = get_supported_implementations();
  386|       |
  387|    379|    std::vector<result<ConversionResult>> results;
  388|    379|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    379|    std::vector<std::vector<ToType>> outputbuffers;
  393|    379|    outputbuffers.reserve(implementations.size());
  394|  1.51k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.13k, False: 379]
  ------------------
  395|  1.13k|      auto impl = implementations[i];
  396|  1.13k|      const ToType canary1{42};
  397|  1.13k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.13k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.13k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.13k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.13k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.13k|          return r != 0;
  404|  1.13k|        } else {
  405|  1.13k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.13k|        }
  407|  1.13k|      }(implret1);
  408|  1.13k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.13k|      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.13k|        const ToType canary2{25};
  414|  1.13k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.13k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.13k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.13k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.13k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.13k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.13k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 507, False: 630]
  |  Branch (423:29): [True: 507, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    507|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    507|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 507]
  ------------------
  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|    507|        }
  440|  1.13k|      }
  441|  1.13k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 507, False: 630]
  ------------------
  442|  1.13k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    379|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 210, False: 169]
  ------------------
  447|    630|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 630, False: 210]
  ------------------
  448|    630|        e.outputhash.clear();
  449|    630|      }
  450|    210|    }
  451|       |
  452|    379|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    379|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 379]
  ------------------
  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|    379|    } else {
  474|    379|      ret.implementations_agree = true;
  475|    379|    }
  476|    379|    return ret;
  477|    379|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|  1.13k|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|  1.13k|        } else {
  405|  1.13k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.13k|        }
  407|  1.13k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_0EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    758|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_36clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    284|    +[](std::span<const char> chardata) {                                      \
  555|    284|      const auto c =                                                           \
  556|    284|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    284|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    284|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    284|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    284|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    284|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    284|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    284|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    284|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    284|  constexpr auto _size = _name.size();                                \
  |  | 1183|    284|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    284|  return _nameof; }()
  ------------------
  561|    284|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    284|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    284|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    284|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    284|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    284|  constexpr auto _size = _name.size();                                \
  |  | 1183|    284|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    284|  return _nameof; }()
  ------------------
  562|    284|      c.fuzz(chardata);                                                        \
  563|    284|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    284|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    284|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    284|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    284|    static const bool do_print_testcase =
  181|    284|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    284|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 284]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    284|    do {
  189|       |      // step 0 - is the input valid?
  190|    284|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    284|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 284]
  |  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|    284|                    From == UtfEncodings::UTF8) {
  198|    284|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 284]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    284|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    284|      const auto [output_length, length_agree] =
  204|    284|          calculate_length(from, inputisvalid);
  205|    284|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 284]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    284|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 158, False: 126]
  |  Branch (208:28): [True: 0, False: 158]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    284|      const auto [written, outputs_agree] =
  215|    284|          do_conversion(from, output_length, inputisvalid);
  216|    284|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 284]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    284|      return;
  221|    284|    } 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|    284|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    284|  validation_result verify_valid_input(FromSpan src) const {
  231|    284|    validation_result ret{};
  232|       |
  233|    284|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    284|    const auto implementations = get_supported_implementations();
  235|    284|    std::vector<simdutf::result> results;
  236|    284|    results.reserve(implementations.size());
  237|       |
  238|    852|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 852, False: 284]
  ------------------
  239|    852|      results.push_back(
  240|    852|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|    852|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|    852|      const bool validation2 =
  245|    852|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|    852|                      src.data(), src.size());
  247|    852|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 852]
  ------------------
  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|    852|    }
  258|       |
  259|    284|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    284|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 284]
  ------------------
  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|    284|    } else {
  273|    284|      ret.implementations_agree = true;
  274|    284|    }
  275|    284|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    284|      return r.error == simdutf::SUCCESS;
  277|    284|    });
  278|    284|    return ret;
  279|    284|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    568|    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|    536|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    536|      return r.error == simdutf::SUCCESS;
  277|    536|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    284|  bool count_the_input(FromSpan src) const {
  290|    284|    const auto implementations = get_supported_implementations();
  291|    284|    std::vector<std::size_t> results;
  292|    284|    results.reserve(implementations.size());
  293|       |
  294|    852|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 852, False: 284]
  ------------------
  295|    852|      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|    852|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|    852|        ret = impl->count_utf8(src.data(), src.size());
  302|    852|      }
  303|    852|      results.push_back(ret);
  304|    852|    }
  305|    284|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    284|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 284]
  ------------------
  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|    284|    return true;
  321|    284|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    568|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    284|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    284|    length_result ret{};
  344|       |
  345|    284|    const auto implementations = get_supported_implementations();
  346|    284|    std::vector<std::size_t> results;
  347|    284|    results.reserve(implementations.size());
  348|       |
  349|    852|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 852, False: 284]
  ------------------
  350|    852|      const auto len = invoke_lengthcalc(impl, src);
  351|    852|      results.push_back(len);
  352|    852|      ret.length.push_back(len);
  353|    852|    }
  354|       |
  355|    284|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    284|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 284]
  ------------------
  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|    284|    } else {
  375|    284|      ret.implementations_agree = true;
  376|    284|    }
  377|    284|    return ret;
  378|    284|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    852|                                FromSpan src) const {
  331|    852|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    852|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    568|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    284|                                  const bool inputisvalid) const {
  383|    284|    conversion_result ret{};
  384|       |
  385|    284|    const auto implementations = get_supported_implementations();
  386|       |
  387|    284|    std::vector<result<ConversionResult>> results;
  388|    284|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    284|    std::vector<std::vector<ToType>> outputbuffers;
  393|    284|    outputbuffers.reserve(implementations.size());
  394|  1.13k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 852, False: 284]
  ------------------
  395|    852|      auto impl = implementations[i];
  396|    852|      const ToType canary1{42};
  397|    852|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    852|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    852|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    852|      const auto success = [](const ConversionResult& r) -> bool {
  402|    852|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    852|          return r != 0;
  404|    852|        } else {
  405|    852|          return r.error == simdutf::error_code::SUCCESS;
  406|    852|        }
  407|    852|      }(implret1);
  408|    852|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    852|      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|    852|        const ToType canary2{25};
  414|    852|        const auto outputbuffer_first_run = outputbuffer;
  415|    852|        std::ranges::fill(outputbuffer, canary2);
  416|    852|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    852|                                          src.size(), outputbuffer.data());
  418|       |
  419|    852|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 852]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    852|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 378, False: 474]
  |  Branch (423:29): [True: 378, False: 0]
  ------------------
  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|    852|      }
  441|    852|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 378, False: 474]
  ------------------
  442|    852|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    284|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 158, False: 126]
  ------------------
  447|    474|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 474, False: 158]
  ------------------
  448|    474|        e.outputhash.clear();
  449|    474|      }
  450|    158|    }
  451|       |
  452|    284|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    284|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 284]
  ------------------
  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|    284|    } else {
  474|    284|      ret.implementations_agree = true;
  475|    284|    }
  476|    284|    return ret;
  477|    284|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|    852|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|    852|        } else {
  405|    852|          return r.error == simdutf::error_code::SUCCESS;
  406|    852|        }
  407|    852|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_1EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDsEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    568|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_37clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    382|    +[](std::span<const char> chardata) {                                      \
  555|    382|      const auto c =                                                           \
  556|    382|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    382|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    382|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    382|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    382|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    382|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    382|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    382|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    382|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    382|  constexpr auto _size = _name.size();                                \
  |  | 1183|    382|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    382|  return _nameof; }()
  ------------------
  561|    382|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    382|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    382|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    382|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    382|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    382|  constexpr auto _size = _name.size();                                \
  |  | 1183|    382|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    382|  return _nameof; }()
  ------------------
  562|    382|      c.fuzz(chardata);                                                        \
  563|    382|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    382|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    382|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    382|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    382|    static const bool do_print_testcase =
  181|    382|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    382|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 382]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    382|    do {
  189|       |      // step 0 - is the input valid?
  190|    382|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    382|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 382]
  |  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|    382|                    From == UtfEncodings::UTF8) {
  198|    382|        if (!count_the_input(from) && !allow_implementations_to_differ)
  ------------------
  |  Branch (198:13): [True: 0, False: 382]
  |  Branch (198:39): [True: 0, Folded]
  ------------------
  199|      0|          break;
  200|    382|      }
  201|       |
  202|       |      // step 2 - what is the required size of the output?
  203|    382|      const auto [output_length, length_agree] =
  204|    382|          calculate_length(from, inputisvalid);
  205|    382|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 382]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    382|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 206, False: 176]
  |  Branch (208:28): [True: 0, False: 206]
  ------------------
  209|       |        // don't run the conversion step, it requires valid input
  210|      0|        return;
  211|      0|      }
  212|       |
  213|       |      // step 3 - run the conversion
  214|    382|      const auto [written, outputs_agree] =
  215|    382|          do_conversion(from, output_length, inputisvalid);
  216|    382|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 382]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    382|      return;
  221|    382|    } 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|    382|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  230|    382|  validation_result verify_valid_input(FromSpan src) const {
  231|    382|    validation_result ret{};
  232|       |
  233|    382|    auto input_validation = ValidationFunctionTrait<From>::ValidationWithErrors;
  234|    382|    const auto implementations = get_supported_implementations();
  235|    382|    std::vector<simdutf::result> results;
  236|    382|    results.reserve(implementations.size());
  237|       |
  238|  1.14k|    for (auto impl : implementations) {
  ------------------
  |  Branch (238:20): [True: 1.14k, False: 382]
  ------------------
  239|  1.14k|      results.push_back(
  240|  1.14k|          std::invoke(input_validation, impl, src.data(), src.size()));
  241|       |
  242|       |      // make sure the validation variant that returns a bool agrees
  243|  1.14k|      const bool validation1 = results.back().error == simdutf::SUCCESS;
  244|  1.14k|      const bool validation2 =
  245|  1.14k|          std::invoke(ValidationFunctionTrait<From>::Validation, impl,
  246|  1.14k|                      src.data(), src.size());
  247|  1.14k|      if (validation1 != validation2) {
  ------------------
  |  Branch (247:11): [True: 0, False: 1.14k]
  ------------------
  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.14k|    }
  258|       |
  259|    382|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  260|    382|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (260:9): [True: 0, False: 382]
  ------------------
  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|    382|    } else {
  273|    382|      ret.implementations_agree = true;
  274|    382|    }
  275|    382|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    382|      return r.error == simdutf::SUCCESS;
  277|    382|    });
  278|    382|    return ret;
  279|    382|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE18verify_valid_inputIvQneT_LS0_4EEENSB_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clIS7_S7_EEDaSJ_SM_:
  259|    764|    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|    734|    ret.valid = std::ranges::all_of(results, [](const simdutf::result& r) {
  276|    734|      return r.error == simdutf::SUCCESS;
  277|    734|    });
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEE:
  289|    382|  bool count_the_input(FromSpan src) const {
  290|    382|    const auto implementations = get_supported_implementations();
  291|    382|    std::vector<std::size_t> results;
  292|    382|    results.reserve(implementations.size());
  293|       |
  294|  1.14k|    for (auto impl : implementations) {
  ------------------
  |  Branch (294:20): [True: 1.14k, False: 382]
  ------------------
  295|  1.14k|      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.14k|      } else if constexpr (From == UtfEncodings::UTF8) {
  301|  1.14k|        ret = impl->count_utf8(src.data(), src.size());
  302|  1.14k|      }
  303|  1.14k|      results.push_back(ret);
  304|  1.14k|    }
  305|    382|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  306|    382|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (306:9): [True: 0, False: 382]
  ------------------
  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|    382|    return true;
  321|    382|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE15count_the_inputENSt3__14spanIS3_Lm18446744073709551615EEEENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  305|    764|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    382|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    382|    length_result ret{};
  344|       |
  345|    382|    const auto implementations = get_supported_implementations();
  346|    382|    std::vector<std::size_t> results;
  347|    382|    results.reserve(implementations.size());
  348|       |
  349|  1.14k|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 1.14k, False: 382]
  ------------------
  350|  1.14k|      const auto len = invoke_lengthcalc(impl, src);
  351|  1.14k|      results.push_back(len);
  352|  1.14k|      ret.length.push_back(len);
  353|  1.14k|    }
  354|       |
  355|    382|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    382|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 382]
  ------------------
  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|    382|    } else {
  375|    382|      ret.implementations_agree = true;
  376|    382|    }
  377|    382|    return ret;
  378|    382|  }
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSF_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|  1.14k|                                FromSpan src) const {
  331|  1.14k|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|  1.14k|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSH_SK_:
  355|    764|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEb:
  382|    382|                                  const bool inputisvalid) const {
  383|    382|    conversion_result ret{};
  384|       |
  385|    382|    const auto implementations = get_supported_implementations();
  386|       |
  387|    382|    std::vector<result<ConversionResult>> results;
  388|    382|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    382|    std::vector<std::vector<ToType>> outputbuffers;
  393|    382|    outputbuffers.reserve(implementations.size());
  394|  1.52k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 1.14k, False: 382]
  ------------------
  395|  1.14k|      auto impl = implementations[i];
  396|  1.14k|      const ToType canary1{42};
  397|  1.14k|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|  1.14k|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|  1.14k|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|  1.14k|      const auto success = [](const ConversionResult& r) -> bool {
  402|  1.14k|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|  1.14k|          return r != 0;
  404|  1.14k|        } else {
  405|  1.14k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.14k|        }
  407|  1.14k|      }(implret1);
  408|  1.14k|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|  1.14k|      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.14k|        const ToType canary2{25};
  414|  1.14k|        const auto outputbuffer_first_run = outputbuffer;
  415|  1.14k|        std::ranges::fill(outputbuffer, canary2);
  416|  1.14k|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|  1.14k|                                          src.size(), outputbuffer.data());
  418|       |
  419|  1.14k|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 1.14k]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|  1.14k|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 528, False: 618]
  |  Branch (423:29): [True: 528, False: 0]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    528|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    528|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 528]
  ------------------
  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|    528|        }
  440|  1.14k|      }
  441|  1.14k|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 528, False: 618]
  ------------------
  442|  1.14k|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    382|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 206, False: 176]
  ------------------
  447|    618|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 618, False: 206]
  ------------------
  448|    618|        e.outputhash.clear();
  449|    618|      }
  450|    206|    }
  451|       |
  452|    382|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    382|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 382]
  ------------------
  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|    382|    } else {
  474|    382|      ret.implementations_agree = true;
  475|    382|    }
  476|    382|    return ret;
  477|    382|  }
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKS7_E_clESM_:
  401|  1.14k|      const auto success = [](const ConversionResult& r) -> bool {
  402|       |        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|       |          return r != 0;
  404|  1.14k|        } else {
  405|  1.14k|          return r.error == simdutf::error_code::SUCCESS;
  406|  1.14k|        }
  407|  1.14k|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings2ELS0_3EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFNS1_6resultES4_mPDiEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSC_6vectorImNSC_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultIS7_ESU_EEDaSN_SQ_:
  452|    764|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_38clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     63|    +[](std::span<const char> chardata) {                                      \
  555|     63|      const auto c =                                                           \
  556|     63|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     63|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     63|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     63|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     63|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     63|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     63|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     63|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     63|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     63|  constexpr auto _size = _name.size();                                \
  |  | 1183|     63|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     63|  return _nameof; }()
  ------------------
  561|     63|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     63|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     63|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     63|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     63|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     63|  constexpr auto _size = _name.size();                                \
  |  | 1183|     63|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     63|  return _nameof; }()
  ------------------
  562|     63|      c.fuzz(chardata);                                                        \
  563|     63|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE4fuzzENSt3__14spanIS5_Lm18446744073709551615EEE:
  175|     63|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|     63|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|     63|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|     63|    static const bool do_print_testcase =
  181|     63|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|     63|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 63]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|     63|    do {
  189|       |      // step 0 - is the input valid?
  190|     63|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|     63|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 63]
  |  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|     63|      const auto [output_length, length_agree] =
  204|     63|          calculate_length(from, inputisvalid);
  205|     63|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 63]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|     63|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 0, False: 63]
  |  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|     63|      const auto [written, outputs_agree] =
  215|     63|          do_conversion(from, output_length, inputisvalid);
  216|     63|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 63]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|     63|      return;
  221|     63|    } 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|     63|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE18verify_valid_inputIvQeqT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEE:
  283|     63|  validation_result verify_valid_input(FromSpan) const {
  284|       |    // all latin1 input is valid. there is no simdutf validation function for
  285|       |    // it.
  286|     63|    return validation_result{.valid = true, .implementations_agree = true};
  287|     63|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEb:
  342|     63|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|     63|    length_result ret{};
  344|       |
  345|     63|    const auto implementations = get_supported_implementations();
  346|     63|    std::vector<std::size_t> results;
  347|     63|    results.reserve(implementations.size());
  348|       |
  349|    189|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 189, False: 63]
  ------------------
  350|    189|      const auto len = invoke_lengthcalc(impl, src);
  351|    189|      results.push_back(len);
  352|    189|      ret.length.push_back(len);
  353|    189|    }
  354|       |
  355|     63|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|     63|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 63]
  ------------------
  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|     63|    } else {
  375|     63|      ret.implementations_agree = true;
  376|     63|    }
  377|     63|    return ret;
  378|     63|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSE_NSt3__14spanIS5_Lm18446744073709551615EEE:
  338|    189|                                FromSpan src) const {
  339|    189|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    189|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    126|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|     63|                                  const bool inputisvalid) const {
  383|     63|    conversion_result ret{};
  384|       |
  385|     63|    const auto implementations = get_supported_implementations();
  386|       |
  387|     63|    std::vector<result<ConversionResult>> results;
  388|     63|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|     63|    std::vector<std::vector<ToType>> outputbuffers;
  393|     63|    outputbuffers.reserve(implementations.size());
  394|    252|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 189, False: 63]
  ------------------
  395|    189|      auto impl = implementations[i];
  396|    189|      const ToType canary1{42};
  397|    189|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    189|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    189|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    189|      const auto success = [](const ConversionResult& r) -> bool {
  402|    189|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    189|          return r != 0;
  404|    189|        } else {
  405|    189|          return r.error == simdutf::error_code::SUCCESS;
  406|    189|        }
  407|    189|      }(implret1);
  408|    189|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    189|      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|    189|        const ToType canary2{25};
  414|    189|        const auto outputbuffer_first_run = outputbuffer;
  415|    189|        std::ranges::fill(outputbuffer, canary2);
  416|    189|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    189|                                          src.size(), outputbuffer.data());
  418|       |
  419|    189|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 189]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    189|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 189, False: 0]
  |  Branch (423:29): [True: 183, False: 6]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    183|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    183|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 183]
  ------------------
  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|    183|        }
  440|    189|      }
  441|    189|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 183, False: 6]
  ------------------
  442|    189|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|     63|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 0, False: 63]
  ------------------
  447|      0|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 0, False: 0]
  ------------------
  448|      0|        e.outputhash.clear();
  449|      0|      }
  450|      0|    }
  451|       |
  452|     63|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|     63|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 63]
  ------------------
  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|     63|    } else {
  474|     63|      ret.implementations_agree = true;
  475|     63|    }
  476|     63|    return ret;
  477|     63|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    189|      const auto success = [](const ConversionResult& r) -> bool {
  402|    189|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    189|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    189|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings4ELS0_3EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDiEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    126|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_39clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     50|    +[](std::span<const char> chardata) {                                      \
  555|     50|      const auto c =                                                           \
  556|     50|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     50|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     50|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     50|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     50|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     50|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     50|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     50|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     50|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     50|  constexpr auto _size = _name.size();                                \
  |  | 1183|     50|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     50|  return _nameof; }()
  ------------------
  561|     50|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     50|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     50|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     50|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     50|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     50|  constexpr auto _size = _name.size();                                \
  |  | 1183|     50|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     50|  return _nameof; }()
  ------------------
  562|     50|      c.fuzz(chardata);                                                        \
  563|     50|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE4fuzzENSt3__14spanIS5_Lm18446744073709551615EEE:
  175|     50|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|     50|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|     50|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|     50|    static const bool do_print_testcase =
  181|     50|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|     50|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 50]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|     50|    do {
  189|       |      // step 0 - is the input valid?
  190|     50|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|     50|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 50]
  |  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|     50|      const auto [output_length, length_agree] =
  204|     50|          calculate_length(from, inputisvalid);
  205|     50|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 50]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|     50|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 0, False: 50]
  |  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|     50|      const auto [written, outputs_agree] =
  215|     50|          do_conversion(from, output_length, inputisvalid);
  216|     50|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 50]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|     50|      return;
  221|     50|    } 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|     50|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE18verify_valid_inputIvQeqT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEE:
  283|     50|  validation_result verify_valid_input(FromSpan) const {
  284|       |    // all latin1 input is valid. there is no simdutf validation function for
  285|       |    // it.
  286|     50|    return validation_result{.valid = true, .implementations_agree = true};
  287|     50|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEb:
  342|     50|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|     50|    length_result ret{};
  344|       |
  345|     50|    const auto implementations = get_supported_implementations();
  346|     50|    std::vector<std::size_t> results;
  347|     50|    results.reserve(implementations.size());
  348|       |
  349|    150|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 150, False: 50]
  ------------------
  350|    150|      const auto len = invoke_lengthcalc(impl, src);
  351|    150|      results.push_back(len);
  352|    150|      ret.length.push_back(len);
  353|    150|    }
  354|       |
  355|     50|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|     50|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 50]
  ------------------
  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|     50|    } else {
  375|     50|      ret.implementations_agree = true;
  376|     50|    }
  377|     50|    return ret;
  378|     50|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSE_NSt3__14spanIS5_Lm18446744073709551615EEE:
  338|    150|                                FromSpan src) const {
  339|    150|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    150|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    100|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|     50|                                  const bool inputisvalid) const {
  383|     50|    conversion_result ret{};
  384|       |
  385|     50|    const auto implementations = get_supported_implementations();
  386|       |
  387|     50|    std::vector<result<ConversionResult>> results;
  388|     50|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|     50|    std::vector<std::vector<ToType>> outputbuffers;
  393|     50|    outputbuffers.reserve(implementations.size());
  394|    200|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 150, False: 50]
  ------------------
  395|    150|      auto impl = implementations[i];
  396|    150|      const ToType canary1{42};
  397|    150|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    150|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    150|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    150|      const auto success = [](const ConversionResult& r) -> bool {
  402|    150|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    150|          return r != 0;
  404|    150|        } else {
  405|    150|          return r.error == simdutf::error_code::SUCCESS;
  406|    150|        }
  407|    150|      }(implret1);
  408|    150|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    150|      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|    150|        const ToType canary2{25};
  414|    150|        const auto outputbuffer_first_run = outputbuffer;
  415|    150|        std::ranges::fill(outputbuffer, canary2);
  416|    150|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    150|                                          src.size(), outputbuffer.data());
  418|       |
  419|    150|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 150]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    150|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 150, False: 0]
  |  Branch (423:29): [True: 144, False: 6]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    144|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    144|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 144]
  ------------------
  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|    144|        }
  440|    150|      }
  441|    150|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 144, False: 6]
  ------------------
  442|    150|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|     50|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 0, False: 50]
  ------------------
  447|      0|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 0, False: 0]
  ------------------
  448|      0|        e.outputhash.clear();
  449|      0|      }
  450|      0|    }
  451|       |
  452|     50|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|     50|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 50]
  ------------------
  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|     50|    } else {
  474|     50|      ret.implementations_agree = true;
  475|     50|    }
  476|     50|    return ret;
  477|     50|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    150|      const auto success = [](const ConversionResult& r) -> bool {
  402|    150|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    150|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    150|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings4ELS0_0EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    100|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_40clENSt3__14spanIKcLm18446744073709551615EEE:
  554|     44|    +[](std::span<const char> chardata) {                                      \
  555|     44|      const auto c =                                                           \
  556|     44|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|     44|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|     44|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|     44|              &I::lenfunc, &I::conversionfunc,                                 \
  560|     44|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|     44|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     44|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     44|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     44|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     44|  constexpr auto _size = _name.size();                                \
  |  | 1183|     44|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     44|  return _nameof; }()
  ------------------
  561|     44|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|     44|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|     44|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|     44|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|     44|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|     44|  constexpr auto _size = _name.size();                                \
  |  | 1183|     44|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|     44|  return _nameof; }()
  ------------------
  562|     44|      c.fuzz(chardata);                                                        \
  563|     44|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE4fuzzENSt3__14spanIS5_Lm18446744073709551615EEE:
  175|     44|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|     44|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|     44|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|     44|    static const bool do_print_testcase =
  181|     44|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|     44|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 44]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|     44|    do {
  189|       |      // step 0 - is the input valid?
  190|     44|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|     44|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 44]
  |  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|     44|      const auto [output_length, length_agree] =
  204|     44|          calculate_length(from, inputisvalid);
  205|     44|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 44]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|     44|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 0, False: 44]
  |  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|     44|      const auto [written, outputs_agree] =
  215|     44|          do_conversion(from, output_length, inputisvalid);
  216|     44|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 44]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|     44|      return;
  221|     44|    } 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|     44|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE18verify_valid_inputIvQeqT_LS0_4EEENSA_17validation_resultENSt3__14spanIS5_Lm18446744073709551615EEE:
  283|     44|  validation_result verify_valid_input(FromSpan) const {
  284|       |    // all latin1 input is valid. there is no simdutf validation function for
  285|       |    // it.
  286|     44|    return validation_result{.valid = true, .implementations_agree = true};
  287|     44|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEb:
  342|     44|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|     44|    length_result ret{};
  344|       |
  345|     44|    const auto implementations = get_supported_implementations();
  346|     44|    std::vector<std::size_t> results;
  347|     44|    results.reserve(implementations.size());
  348|       |
  349|    132|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 132, False: 44]
  ------------------
  350|    132|      const auto len = invoke_lengthcalc(impl, src);
  351|    132|      results.push_back(len);
  352|    132|      ret.length.push_back(len);
  353|    132|    }
  354|       |
  355|     44|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|     44|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 44]
  ------------------
  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|     44|    } else {
  375|     44|      ret.implementations_agree = true;
  376|     44|    }
  377|     44|    return ret;
  378|     44|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_mEEEmSE_NSt3__14spanIS5_Lm18446744073709551615EEE:
  338|    132|                                FromSpan src) const {
  339|    132|    return std::invoke(lengthcalc, impl, /*src.data(),*/ src.size());
  340|    132|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE16calculate_lengthENSt3__14spanIS5_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|     88|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|     44|                                  const bool inputisvalid) const {
  383|     44|    conversion_result ret{};
  384|       |
  385|     44|    const auto implementations = get_supported_implementations();
  386|       |
  387|     44|    std::vector<result<ConversionResult>> results;
  388|     44|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|     44|    std::vector<std::vector<ToType>> outputbuffers;
  393|     44|    outputbuffers.reserve(implementations.size());
  394|    176|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 132, False: 44]
  ------------------
  395|    132|      auto impl = implementations[i];
  396|    132|      const ToType canary1{42};
  397|    132|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    132|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    132|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    132|      const auto success = [](const ConversionResult& r) -> bool {
  402|    132|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    132|          return r != 0;
  404|    132|        } else {
  405|    132|          return r.error == simdutf::error_code::SUCCESS;
  406|    132|        }
  407|    132|      }(implret1);
  408|    132|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    132|      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|    132|        const ToType canary2{25};
  414|    132|        const auto outputbuffer_first_run = outputbuffer;
  415|    132|        std::ranges::fill(outputbuffer, canary2);
  416|    132|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    132|                                          src.size(), outputbuffer.data());
  418|       |
  419|    132|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 132]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    132|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 132, False: 0]
  |  Branch (423:29): [True: 126, False: 6]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    126|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    126|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 126]
  ------------------
  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|    126|        }
  440|    132|      }
  441|    132|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 126, False: 6]
  ------------------
  442|    132|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|     44|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 0, False: 44]
  ------------------
  447|      0|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 0, False: 0]
  ------------------
  448|      0|        e.outputhash.clear();
  449|      0|      }
  450|      0|    }
  451|       |
  452|     44|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|     44|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 44]
  ------------------
  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|     44|    } else {
  474|     44|      ret.implementations_agree = true;
  475|     44|    }
  476|     44|    return ret;
  477|     44|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    132|      const auto success = [](const ConversionResult& r) -> bool {
  402|    132|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    132|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    132|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings4ELS0_1EMN7simdutf14implementationEKDoFmmEMS2_KDoFmPKcmPDsEE13do_conversionENSt3__14spanIS5_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|     88|    auto neq = [](const auto& a, const auto& b) { return a != b; };
conversion.cpp:_ZZ18populate_functionsvENK4$_41clENSt3__14spanIKcLm18446744073709551615EEE:
  554|    307|    +[](std::span<const char> chardata) {                                      \
  555|    307|      const auto c =                                                           \
  556|    307|          Conversion<ENCODING_FROM_CONVERSION_NAME(&I::conversionfunc),        \
  557|    307|                     ENCODING_TO_CONVERSION_NAME(&I::conversionfunc),          \
  558|    307|                     decltype(&I::lenfunc), decltype(&I::conversionfunc)>{     \
  559|    307|              &I::lenfunc, &I::conversionfunc,                                 \
  560|    307|              std::string{NAMEOF(&I::lenfunc)},                                \
  ------------------
  |  | 1178|    307|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    307|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    307|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    307|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    307|  constexpr auto _size = _name.size();                                \
  |  | 1183|    307|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    307|  return _nameof; }()
  ------------------
  561|    307|              std::string{NAMEOF(&I::conversionfunc)}};                        \
  ------------------
  |  | 1178|    307|#define NAMEOF(...) []() constexpr noexcept {                         \
  |  | 1179|    307|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
  |  | 1180|    307|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
  |  | 1181|    307|  static_assert(!_name.empty(), "Expression does not have a name.");  \
  |  | 1182|    307|  constexpr auto _size = _name.size();                                \
  |  | 1183|    307|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
  |  | 1184|    307|  return _nameof; }()
  ------------------
  562|    307|      c.fuzz(chardata);                                                        \
  563|    307|    }                                                                          \
_ZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE4fuzzENSt3__14spanIS3_Lm18446744073709551615EEE:
  175|    307|  void fuzz(std::span<const char> chardata) const {
  176|       |    // assume the input is aligned to FromType
  177|    307|    const FromSpan from{reinterpret_cast<const FromType*>(chardata.data()),
  178|    307|                        chardata.size() / sizeof(FromType)};
  179|       |
  180|    307|    static const bool do_print_testcase =
  181|    307|        std::getenv("PRINT_FUZZ_CASE") != nullptr;
  182|       |
  183|    307|    if (do_print_testcase) {
  ------------------
  |  Branch (183:9): [True: 0, False: 307]
  ------------------
  184|      0|      dump_testcase(from, std::cerr);
  185|      0|      std::exit(EXIT_SUCCESS);
  186|      0|    }
  187|       |
  188|    307|    do {
  189|       |      // step 0 - is the input valid?
  190|    307|      const auto [inputisvalid, valid_input_agree] = verify_valid_input(from);
  191|    307|      if (!valid_input_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (191:11): [True: 0, False: 307]
  |  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|    307|      const auto [output_length, length_agree] =
  204|    307|          calculate_length(from, inputisvalid);
  205|    307|      if (!length_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (205:11): [True: 0, False: 307]
  |  Branch (205:28): [True: 0, Folded]
  ------------------
  206|      0|        break;
  207|       |
  208|    307|      if (!inputisvalid && name.find("valid") != std::string::npos) {
  ------------------
  |  Branch (208:11): [True: 0, False: 307]
  |  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|    307|      const auto [written, outputs_agree] =
  215|    307|          do_conversion(from, output_length, inputisvalid);
  216|    307|      if (!outputs_agree && !allow_implementations_to_differ)
  ------------------
  |  Branch (216:11): [True: 0, False: 307]
  |  Branch (216:29): [True: 0, Folded]
  ------------------
  217|      0|        break;
  218|       |
  219|       |      // coming this far means no problems were found
  220|    307|      return;
  221|    307|    } 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|    307|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE18verify_valid_inputIvQeqT_LS0_4EEENSA_17validation_resultENSt3__14spanIS3_Lm18446744073709551615EEE:
  283|    307|  validation_result verify_valid_input(FromSpan) const {
  284|       |    // all latin1 input is valid. there is no simdutf validation function for
  285|       |    // it.
  286|    307|    return validation_result{.valid = true, .implementations_agree = true};
  287|    307|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEb:
  342|    307|  length_result calculate_length(FromSpan src, const bool inputisvalid) const {
  343|    307|    length_result ret{};
  344|       |
  345|    307|    const auto implementations = get_supported_implementations();
  346|    307|    std::vector<std::size_t> results;
  347|    307|    results.reserve(implementations.size());
  348|       |
  349|    921|    for (auto impl : implementations) {
  ------------------
  |  Branch (349:20): [True: 921, False: 307]
  ------------------
  350|    921|      const auto len = invoke_lengthcalc(impl, src);
  351|    921|      results.push_back(len);
  352|    921|      ret.length.push_back(len);
  353|    921|    }
  354|       |
  355|    307|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  356|    307|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (356:9): [True: 0, False: 307]
  ------------------
  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|    307|    } else {
  375|    307|      ret.implementations_agree = true;
  376|    307|    }
  377|    307|    return ret;
  378|    307|  }
_ZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE17invoke_lengthcalcIvQsr3stdE14is_invocable_vIT1_PKS2_PKN23ValidationFunctionTraitIXT_EE7RawTypeEmEEEmSE_NSt3__14spanIS3_Lm18446744073709551615EEE:
  330|    921|                                FromSpan src) const {
  331|    921|    return std::invoke(lengthcalc, impl, src.data(), src.size());
  332|    921|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE16calculate_lengthENSt3__14spanIS3_Lm18446744073709551615EEEbENKUlRKT_RKT0_E_clImmEEDaSG_SJ_:
  355|    614|    auto neq = [](const auto& a, const auto& b) { return a != b; };
_ZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEb:
  382|    307|                                  const bool inputisvalid) const {
  383|    307|    conversion_result ret{};
  384|       |
  385|    307|    const auto implementations = get_supported_implementations();
  386|       |
  387|    307|    std::vector<result<ConversionResult>> results;
  388|    307|    results.reserve(implementations.size());
  389|       |
  390|       |    // put the output in a separate allocation to make access violations easier
  391|       |    // to catch
  392|    307|    std::vector<std::vector<ToType>> outputbuffers;
  393|    307|    outputbuffers.reserve(implementations.size());
  394|  1.22k|    for (std::size_t i = 0; i < implementations.size(); ++i) {
  ------------------
  |  Branch (394:29): [True: 921, False: 307]
  ------------------
  395|    921|      auto impl = implementations[i];
  396|    921|      const ToType canary1{42};
  397|    921|      auto& outputbuffer = outputbuffers.emplace_back(outlength.at(i), canary1);
  398|    921|      const auto implret1 = std::invoke(conversion, impl, src.data(),
  399|    921|                                        src.size(), outputbuffer.data());
  400|       |      // was the conversion successful?
  401|    921|      const auto success = [](const ConversionResult& r) -> bool {
  402|    921|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    921|          return r != 0;
  404|    921|        } else {
  405|    921|          return r.error == simdutf::error_code::SUCCESS;
  406|    921|        }
  407|    921|      }(implret1);
  408|    921|      const auto hash1 = FNV1A_hash::as_str(outputbuffer);
  409|    921|      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|    921|        const ToType canary2{25};
  414|    921|        const auto outputbuffer_first_run = outputbuffer;
  415|    921|        std::ranges::fill(outputbuffer, canary2);
  416|    921|        const auto implret2 = std::invoke(conversion, impl, src.data(),
  417|    921|                                          src.size(), outputbuffer.data());
  418|       |
  419|    921|        if (implret1 != implret2) {
  ------------------
  |  Branch (419:13): [True: 0, False: 921]
  ------------------
  420|      0|          std::cerr << "different return value the second time!\n";
  421|      0|          std::abort();
  422|      0|        }
  423|    921|        if (inputisvalid && success) {
  ------------------
  |  Branch (423:13): [True: 921, False: 0]
  |  Branch (423:29): [True: 915, False: 6]
  ------------------
  424|       |          // only care about the output if the input is valid
  425|    915|          const auto hash2 = FNV1A_hash::as_str(outputbuffer);
  426|    915|          if (hash1 != hash2) {
  ------------------
  |  Branch (426:15): [True: 0, False: 915]
  ------------------
  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|    915|        }
  440|    921|      }
  441|    921|      results.emplace_back(implret1, success ? hash1 : "");
  ------------------
  |  Branch (441:38): [True: 915, False: 6]
  ------------------
  442|    921|    }
  443|       |
  444|       |    // do not require implementations to give the same output if
  445|       |    // the input is not valid.
  446|    307|    if (!inputisvalid) {
  ------------------
  |  Branch (446:9): [True: 0, False: 307]
  ------------------
  447|      0|      for (auto& e : results) {
  ------------------
  |  Branch (447:20): [True: 0, False: 0]
  ------------------
  448|      0|        e.outputhash.clear();
  449|      0|      }
  450|      0|    }
  451|       |
  452|    307|    auto neq = [](const auto& a, const auto& b) { return a != b; };
  453|    307|    if (std::ranges::adjacent_find(results, neq) != results.end()) {
  ------------------
  |  Branch (453:9): [True: 0, False: 307]
  ------------------
  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|    307|    } else {
  474|    307|      ret.implementations_agree = true;
  475|    307|    }
  476|    307|    return ret;
  477|    307|  }
_ZZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKmE_clESL_:
  401|    921|      const auto success = [](const ConversionResult& r) -> bool {
  402|    921|        if constexpr (std::is_same_v<ConversionResult, std::size_t>) {
  403|    921|          return r != 0;
  404|       |        } else {
  405|       |          return r.error == simdutf::error_code::SUCCESS;
  406|       |        }
  407|    921|      }(implret1);
_ZZNK10ConversionIL12UtfEncodings4ELS0_2EMN7simdutf14implementationEKDoFmPKcmEMS2_KDoFmS4_mPcEE13do_conversionENSt3__14spanIS3_Lm18446744073709551615EEERKNSB_6vectorImNSB_9allocatorImEEEEbENKUlRKT_RKT0_E_clI6resultImEST_EEDaSM_SP_:
  452|    614|    auto neq = [](const auto& a, const auto& b) { return a != b; };

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

_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.01k|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt25EE4dataEv:
  233|  1.01k|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt25EE4sizeEv:
  235|  1.01k|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK3$_0clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     89|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     89|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     89|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     89|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     89|  constexpr auto _size = _name.size();                                \
 1183|     89|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     89|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_0clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     89|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     89|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     89|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     89|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     89|  constexpr auto _size = _name.size();                                \
 1183|     89|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     89|  return _nameof; }()
_ZNK6nameof7cstringILt30EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|    348|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt30EE4dataEv:
  233|    348|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt30EE4sizeEv:
  235|    348|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK3$_1clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    212|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    212|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    212|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    212|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    212|  constexpr auto _size = _name.size();                                \
 1183|    212|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    212|  return _nameof; }()
_ZNK6nameof7cstringILt24EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|  3.10k|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt24EE4dataEv:
  233|  3.10k|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt24EE4sizeEv:
  235|  3.10k|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK3$_1clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    212|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    212|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    212|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    212|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    212|  constexpr auto _size = _name.size();                                \
 1183|    212|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    212|  return _nameof; }()
_ZNK6nameof7cstringILt29EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|    905|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt29EE4dataEv:
  233|    905|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt29EE4sizeEv:
  235|    905|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK3$_2clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     66|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     66|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     66|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     66|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     66|  constexpr auto _size = _name.size();                                \
 1183|     66|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     66|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_2clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     66|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     66|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     66|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     66|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     66|  constexpr auto _size = _name.size();                                \
 1183|     66|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     66|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_3clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    223|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    223|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    223|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    223|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    223|  constexpr auto _size = _name.size();                                \
 1183|    223|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    223|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_3clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    223|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    223|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    223|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    223|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    223|  constexpr auto _size = _name.size();                                \
 1183|    223|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    223|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_4clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     65|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     65|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     65|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     65|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     65|  constexpr auto _size = _name.size();                                \
 1183|     65|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     65|  return _nameof; }()
_ZNK6nameof7cstringILt23EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|  3.44k|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt23EE4dataEv:
  233|  3.44k|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt23EE4sizeEv:
  235|  3.44k|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK3$_4clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     65|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     65|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     65|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     65|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     65|  constexpr auto _size = _name.size();                                \
 1183|     65|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     65|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_5clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    128|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    128|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    128|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    128|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    128|  constexpr auto _size = _name.size();                                \
 1183|    128|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    128|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_5clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    128|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    128|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    128|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    128|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    128|  constexpr auto _size = _name.size();                                \
 1183|    128|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    128|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_6clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    120|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    120|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    120|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    120|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    120|  constexpr auto _size = _name.size();                                \
 1183|    120|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    120|  return _nameof; }()
_ZNK6nameof7cstringILt22EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|  4.36k|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt22EE4dataEv:
  233|  4.36k|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt22EE4sizeEv:
  235|  4.36k|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK3$_6clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    120|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    120|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    120|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    120|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    120|  constexpr auto _size = _name.size();                                \
 1183|    120|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    120|  return _nameof; }()
_ZNK6nameof7cstringILt27EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|    354|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt27EE4dataEv:
  233|    354|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt27EE4sizeEv:
  235|    354|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK3$_7clENSt3__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_functionsvENK3$_7clENSt3__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_functionsvENK3$_8clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    226|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    226|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    226|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    226|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    226|  constexpr auto _size = _name.size();                                \
 1183|    226|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    226|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_8clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    226|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    226|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    226|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    226|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    226|  constexpr auto _size = _name.size();                                \
 1183|    226|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    226|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_9clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    234|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    234|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    234|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    234|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    234|  constexpr auto _size = _name.size();                                \
 1183|    234|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    234|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK3$_9clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    234|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    234|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    234|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    234|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    234|  constexpr auto _size = _name.size();                                \
 1183|    234|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    234|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_10clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     51|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     51|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     51|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     51|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     51|  constexpr auto _size = _name.size();                                \
 1183|     51|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     51|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_10clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     51|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     51|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     51|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     51|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     51|  constexpr auto _size = _name.size();                                \
 1183|     51|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     51|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_11clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    119|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    119|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    119|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    119|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    119|  constexpr auto _size = _name.size();                                \
 1183|    119|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    119|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_11clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    119|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    119|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    119|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    119|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    119|  constexpr auto _size = _name.size();                                \
 1183|    119|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    119|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_12clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    171|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    171|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    171|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    171|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    171|  constexpr auto _size = _name.size();                                \
 1183|    171|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    171|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_12clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    171|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    171|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    171|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    171|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    171|  constexpr auto _size = _name.size();                                \
 1183|    171|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    171|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_13clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     66|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     66|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     66|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     66|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     66|  constexpr auto _size = _name.size();                                \
 1183|     66|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     66|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_13clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     66|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     66|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     66|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     66|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     66|  constexpr auto _size = _name.size();                                \
 1183|     66|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     66|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_14clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    147|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    147|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    147|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    147|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    147|  constexpr auto _size = _name.size();                                \
 1183|    147|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    147|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_14clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    147|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    147|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    147|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    147|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    147|  constexpr auto _size = _name.size();                                \
 1183|    147|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    147|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_15clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    205|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    205|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    205|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    205|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    205|  constexpr auto _size = _name.size();                                \
 1183|    205|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    205|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_15clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    205|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    205|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    205|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    205|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    205|  constexpr auto _size = _name.size();                                \
 1183|    205|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    205|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_16clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     89|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     89|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     89|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     89|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     89|  constexpr auto _size = _name.size();                                \
 1183|     89|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     89|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_16clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     89|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     89|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     89|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     89|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     89|  constexpr auto _size = _name.size();                                \
 1183|     89|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     89|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_17clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    254|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    254|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    254|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    254|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    254|  constexpr auto _size = _name.size();                                \
 1183|    254|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    254|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_17clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    254|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    254|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    254|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    254|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    254|  constexpr auto _size = _name.size();                                \
 1183|    254|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    254|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_18clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    248|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    248|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    248|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    248|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    248|  constexpr auto _size = _name.size();                                \
 1183|    248|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    248|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_18clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    248|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    248|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    248|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    248|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    248|  constexpr auto _size = _name.size();                                \
 1183|    248|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    248|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_19clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    318|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    318|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    318|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    318|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    318|  constexpr auto _size = _name.size();                                \
 1183|    318|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    318|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_19clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    318|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    318|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    318|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    318|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    318|  constexpr auto _size = _name.size();                                \
 1183|    318|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    318|  return _nameof; }()
_ZNK6nameof7cstringILt21EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|    697|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt21EE4dataEv:
  233|    697|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt21EE4sizeEv:
  235|    697|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK4$_20clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    288|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    288|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    288|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    288|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    288|  constexpr auto _size = _name.size();                                \
 1183|    288|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    288|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_20clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    288|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    288|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    288|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    288|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    288|  constexpr auto _size = _name.size();                                \
 1183|    288|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    288|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_21clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    379|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    379|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    379|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    379|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    379|  constexpr auto _size = _name.size();                                \
 1183|    379|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    379|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_21clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    379|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    379|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    379|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    379|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    379|  constexpr auto _size = _name.size();                                \
 1183|    379|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    379|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_22clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    377|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    377|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    377|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    377|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    377|  constexpr auto _size = _name.size();                                \
 1183|    377|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    377|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_22clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    377|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    377|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    377|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    377|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    377|  constexpr auto _size = _name.size();                                \
 1183|    377|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    377|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_23clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    379|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    379|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    379|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    379|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    379|  constexpr auto _size = _name.size();                                \
 1183|    379|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    379|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_23clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    379|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    379|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    379|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    379|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    379|  constexpr auto _size = _name.size();                                \
 1183|    379|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    379|  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|    265|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt37EE4dataEv:
  233|    265|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt37EE4sizeEv:
  235|    265|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK4$_25clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    181|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    181|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    181|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    181|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    181|  constexpr auto _size = _name.size();                                \
 1183|    181|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    181|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_25clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    181|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    181|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    181|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    181|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    181|  constexpr auto _size = _name.size();                                \
 1183|    181|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    181|  return _nameof; }()
_ZNK6nameof7cstringILt36EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|  1.01k|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt36EE4dataEv:
  233|  1.01k|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt36EE4sizeEv:
  235|  1.01k|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK4$_26clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    358|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    358|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    358|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    358|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    358|  constexpr auto _size = _name.size();                                \
 1183|    358|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    358|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_26clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    358|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    358|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    358|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    358|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    358|  constexpr auto _size = _name.size();                                \
 1183|    358|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    358|  return _nameof; }()
_ZNK6nameof7cstringILt35EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|  1.56k|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt35EE4dataEv:
  233|  1.56k|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt35EE4sizeEv:
  235|  1.56k|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK4$_27clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    141|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    141|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    141|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    141|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    141|  constexpr auto _size = _name.size();                                \
 1183|    141|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    141|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_27clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    141|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    141|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    141|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    141|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    141|  constexpr auto _size = _name.size();                                \
 1183|    141|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    141|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_28clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    204|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    204|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    204|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    204|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    204|  constexpr auto _size = _name.size();                                \
 1183|    204|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    204|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_28clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    204|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    204|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    204|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    204|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    204|  constexpr auto _size = _name.size();                                \
 1183|    204|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    204|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_29clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    339|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    339|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    339|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    339|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    339|  constexpr auto _size = _name.size();                                \
 1183|    339|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    339|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_29clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    339|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    339|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    339|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    339|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    339|  constexpr auto _size = _name.size();                                \
 1183|    339|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    339|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_30clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    204|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    204|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    204|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    204|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    204|  constexpr auto _size = _name.size();                                \
 1183|    204|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    204|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_30clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    204|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    204|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    204|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    204|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    204|  constexpr auto _size = _name.size();                                \
 1183|    204|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    204|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_31clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    296|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    296|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    296|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    296|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    296|  constexpr auto _size = _name.size();                                \
 1183|    296|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    296|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_31clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    296|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    296|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    296|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    296|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    296|  constexpr auto _size = _name.size();                                \
 1183|    296|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    296|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_32clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    329|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    329|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    329|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    329|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    329|  constexpr auto _size = _name.size();                                \
 1183|    329|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    329|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_32clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    329|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    329|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    329|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    329|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    329|  constexpr auto _size = _name.size();                                \
 1183|    329|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    329|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_33clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    446|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    446|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    446|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    446|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    446|  constexpr auto _size = _name.size();                                \
 1183|    446|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    446|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_33clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    446|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    446|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    446|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    446|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    446|  constexpr auto _size = _name.size();                                \
 1183|    446|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    446|  return _nameof; }()
_ZNK6nameof7cstringILt33EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|    828|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt33EE4dataEv:
  233|    828|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt33EE4sizeEv:
  235|    828|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK4$_34clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    250|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    250|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    250|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    250|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    250|  constexpr auto _size = _name.size();                                \
 1183|    250|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    250|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_34clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    250|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    250|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    250|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    250|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    250|  constexpr auto _size = _name.size();                                \
 1183|    250|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    250|  return _nameof; }()
_ZNK6nameof7cstringILt34EEcvNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEEv:
  269|    250|  [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; }
_ZNK6nameof7cstringILt34EE4dataEv:
  233|    250|  [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; }
_ZNK6nameof7cstringILt34EE4sizeEv:
  235|    250|  [[nodiscard]] constexpr size_type size() const noexcept { return N; }
conversion.cpp:_ZZZ18populate_functionsvENK4$_35clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    379|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    379|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    379|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    379|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    379|  constexpr auto _size = _name.size();                                \
 1183|    379|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    379|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_35clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    379|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    379|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    379|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    379|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    379|  constexpr auto _size = _name.size();                                \
 1183|    379|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    379|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_36clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    284|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    284|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    284|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    284|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    284|  constexpr auto _size = _name.size();                                \
 1183|    284|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    284|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_36clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    284|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    284|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    284|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    284|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    284|  constexpr auto _size = _name.size();                                \
 1183|    284|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    284|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_37clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    382|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    382|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    382|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    382|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    382|  constexpr auto _size = _name.size();                                \
 1183|    382|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    382|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_37clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    382|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    382|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    382|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    382|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    382|  constexpr auto _size = _name.size();                                \
 1183|    382|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    382|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_38clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     63|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     63|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     63|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     63|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     63|  constexpr auto _size = _name.size();                                \
 1183|     63|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     63|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_38clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     63|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     63|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     63|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     63|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     63|  constexpr auto _size = _name.size();                                \
 1183|     63|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     63|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_39clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     50|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     50|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     50|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     50|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     50|  constexpr auto _size = _name.size();                                \
 1183|     50|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     50|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_39clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     50|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     50|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     50|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     50|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     50|  constexpr auto _size = _name.size();                                \
 1183|     50|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     50|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_40clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|     44|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     44|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     44|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     44|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     44|  constexpr auto _size = _name.size();                                \
 1183|     44|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     44|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_40clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|     44|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|     44|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|     44|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|     44|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|     44|  constexpr auto _size = _name.size();                                \
 1183|     44|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|     44|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_41clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE1_clEv:
 1178|    307|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    307|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    307|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    307|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    307|  constexpr auto _size = _name.size();                                \
 1183|    307|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    307|  return _nameof; }()
conversion.cpp:_ZZZ18populate_functionsvENK4$_41clENSt3__14spanIKcLm18446744073709551615EEEENKUlvE2_clEv:
 1178|    307|#define NAMEOF(...) []() constexpr noexcept {                         \
 1179|    307|  ::std::void_t<decltype(__VA_ARGS__)>();                             \
 1180|    307|  constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \
 1181|    307|  static_assert(!_name.empty(), "Expression does not have a name.");  \
 1182|    307|  constexpr auto _size = _name.size();                                \
 1183|    307|  constexpr auto _nameof = ::nameof::cstring<_size>{_name};           \
 1184|    307|  return _nameof; }()

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

_ZNK7simdutf14implementation4nameEv:
 5092|      4|  virtual std::string_view name() const noexcept { return _name; }
_ZNK7simdutf14implementation25required_instruction_setsEv:
 5143|      8|  virtual uint32_t required_instruction_sets() const {
 5144|      8|    return _required_instruction_sets;
 5145|      8|  }
_ZNK7simdutf14implementation24utf16_length_from_latin1Em:
 6333|    282|  utf16_length_from_latin1(size_t length) const noexcept {
 6334|    282|    return length;
 6335|    282|  }
_ZNK7simdutf14implementation24latin1_length_from_utf32Em:
 6521|    879|  latin1_length_from_utf32(size_t length) const noexcept {
 6522|    879|    return length;
 6523|    879|  }
_ZNK7simdutf14implementation24latin1_length_from_utf16Em:
 6558|  1.14k|  latin1_length_from_utf16(size_t length) const noexcept {
 6559|  1.14k|    return length;
 6560|  1.14k|  }
_ZNK7simdutf14implementation24utf32_length_from_latin1Em:
 6591|    189|  utf32_length_from_latin1(size_t length) const noexcept {
 6592|    189|    return length;
 6593|    189|  }
_ZN7simdutf14implementationC2EPKcS2_j:
 7029|      4|      : _name(name), _description(description),
 7030|      4|        _required_instruction_sets(required_instruction_sets) {}
_ZN7simdutf8internal29available_implementation_listC2Ev:
 7061|      1|  simdutf_really_inline available_implementation_list() {}

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

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

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

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

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf87convertEPKcmPc:
   64|  1.71k|                                     char *utf8_output) {
   65|  1.71k|  return convert(reinterpret_cast<const unsigned char *>(buf), len,
   66|  1.71k|                 utf8_output);
   67|  1.71k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf87convertIPKhPcQaasr7simdutf6detailE22indexes_into_byte_likeIT_Esr7simdutf6detailE26index_assignable_from_charIT0_EEEmS7_mS8_:
   15|  1.71k|                                   OutputPtr utf8_output) {
   16|       |  // const unsigned char *data = reinterpret_cast<const unsigned char *>(buf);
   17|  1.71k|  size_t pos = 0;
   18|  1.71k|  size_t utf8_pos = 0;
   19|       |
   20|  7.16M|  while (pos < len) {
  ------------------
  |  Branch (20:10): [True: 7.16M, False: 1.71k]
  ------------------
   21|       |#if SIMDUTF_CPLUSPLUS23
   22|       |    if !consteval
   23|       |#endif
   24|  7.16M|    {
   25|       |      // try to convert the next block of 16 ASCII bytes
   26|  7.16M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (26:11): [True: 7.15M, False: 15.4k]
  ------------------
   27|       |                             // they are ascii
   28|  7.15M|        uint64_t v1;
   29|  7.15M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
   30|  7.15M|        uint64_t v2;
   31|  7.15M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   32|  7.15M|        uint64_t v{v1 |
   33|  7.15M|                   v2}; // We are only interested in these bits: 1000 1000 1000
   34|       |                        // 1000, so it makes sense to concatenate everything
   35|  7.15M|        if ((v & 0x8080808080808080) ==
  ------------------
  |  Branch (35:13): [True: 2.70M, False: 4.44M]
  ------------------
   36|  7.15M|            0) { // if NONE of these are set, e.g. all of them are zero, then
   37|       |                 // everything is ASCII
   38|  2.70M|          size_t final_pos = pos + 16;
   39|  46.0M|          while (pos < final_pos) {
  ------------------
  |  Branch (39:18): [True: 43.3M, False: 2.70M]
  ------------------
   40|  43.3M|            utf8_output[utf8_pos++] = char(data[pos]);
   41|  43.3M|            pos++;
   42|  43.3M|          }
   43|  2.70M|          continue;
   44|  2.70M|        }
   45|  7.15M|      } // if (pos + 16 <= len)
   46|  7.16M|    } // !consteval scope
   47|       |
   48|  4.46M|    unsigned char byte = data[pos];
   49|  4.46M|    if ((byte & 0x80) == 0) { // if ASCII
  ------------------
  |  Branch (49:9): [True: 2.75M, False: 1.70M]
  ------------------
   50|       |      // will generate one UTF-8 bytes
   51|  2.75M|      utf8_output[utf8_pos++] = char(byte);
   52|  2.75M|      pos++;
   53|  2.75M|    } else {
   54|       |      // will generate two UTF-8 bytes
   55|  1.70M|      utf8_output[utf8_pos++] = char((byte >> 6) | 0b11000000);
   56|  1.70M|      utf8_output[utf8_pos++] = char((byte & 0b111111) | 0b10000000);
   57|  1.70M|      pos++;
   58|  1.70M|    }
   59|  4.46M|  } // while
   60|  1.71k|  return utf8_pos;
   61|  1.71k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf823utf8_length_from_latin1IPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEEmS6_m:
  150|    307|utf8_length_from_latin1(InputPtr input, size_t length) noexcept {
  151|    307|  size_t answer = length;
  152|    307|  size_t i = 0;
  153|       |
  154|       |#if SIMDUTF_CPLUSPLUS23
  155|       |  if !consteval
  156|       |#endif
  157|    307|  {
  158|    307|    auto pop = [](uint64_t v) {
  159|    307|      return (size_t)(((v >> 7) & UINT64_C(0x0101010101010101)) *
  160|    307|                          UINT64_C(0x0101010101010101) >>
  161|    307|                      56);
  162|    307|    };
  163|   746k|    for (; i + 32 <= length; i += 32) {
  ------------------
  |  Branch (163:12): [True: 746k, False: 307]
  ------------------
  164|   746k|      uint64_t v;
  165|   746k|      memcpy(&v, input + i, 8);
  166|   746k|      answer += pop(v);
  167|   746k|      memcpy(&v, input + i + 8, sizeof(v));
  168|   746k|      answer += pop(v);
  169|   746k|      memcpy(&v, input + i + 16, sizeof(v));
  170|   746k|      answer += pop(v);
  171|   746k|      memcpy(&v, input + i + 24, sizeof(v));
  172|   746k|      answer += pop(v);
  173|   746k|    }
  174|    591|    for (; i + 8 <= length; i += 8) {
  ------------------
  |  Branch (174:12): [True: 284, False: 307]
  ------------------
  175|    284|      uint64_t v;
  176|    284|      memcpy(&v, input + i, sizeof(v));
  177|    284|      answer += pop(v);
  178|    284|    }
  179|    307|  } // !consteval scope
  180|    886|  for (; i + 1 <= length; i += 1) {
  ------------------
  |  Branch (180:10): [True: 579, False: 307]
  ------------------
  181|    579|    answer += static_cast<uint8_t>(input[i]) >> 7;
  182|    579|  }
  183|    307|  return answer;
  184|    307|}
simdutf.cpp:_ZZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf823utf8_length_from_latin1IPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEEmS6_mENKUlmE_clEm:
  158|  2.98M|    auto pop = [](uint64_t v) {
  159|  2.98M|      return (size_t)(((v >> 7) & UINT64_C(0x0101010101010101)) *
  160|       |                          UINT64_C(0x0101010101010101) >>
  161|  2.98M|                      56);
  162|  2.98M|    };

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

_ZN7simdutf6scalar5utf168validateILNS_10endiannessE0EEEbPKDsm:
   22|  3.85k|validate(const char16_t *data, size_t len) noexcept {
   23|  3.85k|  uint64_t pos = 0;
   24|  20.8M|  while (pos < len) {
  ------------------
  |  Branch (24:10): [True: 20.8M, False: 2.55k]
  ------------------
   25|  20.8M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(data[pos]);
   26|  20.8M|    if ((word & 0xF800) == 0xD800) {
  ------------------
  |  Branch (26:9): [True: 20.5k, False: 20.8M]
  ------------------
   27|  20.5k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (27:11): [True: 231, False: 20.2k]
  ------------------
   28|    231|        return false;
   29|    231|      }
   30|  20.2k|      char16_t diff = char16_t(word - 0xD800);
   31|  20.2k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (31:11): [True: 403, False: 19.8k]
  ------------------
   32|    403|        return false;
   33|    403|      }
   34|  19.8k|      char16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (34:28): [Folded, False: 19.8k]
  ------------------
   35|  19.8k|                               ? u16_swap_bytes(data[pos + 1])
   36|  19.8k|                               : data[pos + 1];
   37|  19.8k|      char16_t diff2 = char16_t(next_word - 0xDC00);
   38|  19.8k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (38:11): [True: 666, False: 19.2k]
  ------------------
   39|    666|        return false;
   40|    666|      }
   41|  19.2k|      pos += 2;
   42|  20.8M|    } else {
   43|  20.8M|      pos++;
   44|  20.8M|    }
   45|  20.8M|  }
   46|  2.55k|  return true;
   47|  3.85k|}
_ZN7simdutf6scalar5utf168validateILNS_10endiannessE1EEEbPKDsm:
   22|  3.64k|validate(const char16_t *data, size_t len) noexcept {
   23|  3.64k|  uint64_t pos = 0;
   24|  21.4M|  while (pos < len) {
  ------------------
  |  Branch (24:10): [True: 21.4M, False: 2.49k]
  ------------------
   25|  21.4M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(data[pos]);
   26|  21.4M|    if ((word & 0xF800) == 0xD800) {
  ------------------
  |  Branch (26:9): [True: 23.2k, False: 21.3M]
  ------------------
   27|  23.2k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (27:11): [True: 231, False: 23.0k]
  ------------------
   28|    231|        return false;
   29|    231|      }
   30|  23.0k|      char16_t diff = char16_t(word - 0xD800);
   31|  23.0k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (31:11): [True: 413, False: 22.6k]
  ------------------
   32|    413|        return false;
   33|    413|      }
   34|  22.6k|      char16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (34:28): [True: 22.6k, Folded]
  ------------------
   35|  22.6k|                               ? u16_swap_bytes(data[pos + 1])
   36|  22.6k|                               : data[pos + 1];
   37|  22.6k|      char16_t diff2 = char16_t(next_word - 0xDC00);
   38|  22.6k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (38:11): [True: 512, False: 22.1k]
  ------------------
   39|    512|        return false;
   40|    512|      }
   41|  22.1k|      pos += 2;
   42|  21.3M|    } else {
   43|  21.3M|      pos++;
   44|  21.3M|    }
   45|  21.4M|  }
   46|  2.49k|  return true;
   47|  3.64k|}
_ZN7simdutf6scalar5utf1620validate_with_errorsILNS_10endiannessE0EEENS_6resultEPKDsm:
   51|  4.14k|validate_with_errors(const char16_t *data, size_t len) noexcept {
   52|  4.14k|  size_t pos = 0;
   53|  20.8M|  while (pos < len) {
  ------------------
  |  Branch (53:10): [True: 20.8M, False: 2.55k]
  ------------------
   54|  20.8M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(data[pos]);
   55|  20.8M|    if ((word & 0xF800) == 0xD800) {
  ------------------
  |  Branch (55:9): [True: 20.8k, False: 20.8M]
  ------------------
   56|  20.8k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (56:11): [True: 231, False: 20.6k]
  ------------------
   57|    231|        return result(error_code::SURROGATE, pos);
   58|    231|      }
   59|  20.6k|      char16_t diff = char16_t(word - 0xD800);
   60|  20.6k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (60:11): [True: 528, False: 20.1k]
  ------------------
   61|    528|        return result(error_code::SURROGATE, pos);
   62|    528|      }
   63|  20.1k|      char16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (63:28): [Folded, False: 20.1k]
  ------------------
   64|  20.1k|                               ? u16_swap_bytes(data[pos + 1])
   65|  20.1k|                               : data[pos + 1];
   66|  20.1k|      char16_t diff2 = uint16_t(next_word - 0xDC00);
   67|  20.1k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (67:11): [True: 831, False: 19.2k]
  ------------------
   68|    831|        return result(error_code::SURROGATE, pos);
   69|    831|      }
   70|  19.2k|      pos += 2;
   71|  20.8M|    } else {
   72|  20.8M|      pos++;
   73|  20.8M|    }
   74|  20.8M|  }
   75|  2.55k|  return result(error_code::SUCCESS, pos);
   76|  4.14k|}
_ZN7simdutf6scalar5utf1620validate_with_errorsILNS_10endiannessE1EEENS_6resultEPKDsm:
   51|  3.88k|validate_with_errors(const char16_t *data, size_t len) noexcept {
   52|  3.88k|  size_t pos = 0;
   53|  21.4M|  while (pos < len) {
  ------------------
  |  Branch (53:10): [True: 21.4M, False: 2.49k]
  ------------------
   54|  21.4M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(data[pos]);
   55|  21.4M|    if ((word & 0xF800) == 0xD800) {
  ------------------
  |  Branch (55:9): [True: 23.5k, False: 21.3M]
  ------------------
   56|  23.5k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (56:11): [True: 231, False: 23.3k]
  ------------------
   57|    231|        return result(error_code::SURROGATE, pos);
   58|    231|      }
   59|  23.3k|      char16_t diff = char16_t(word - 0xD800);
   60|  23.3k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (60:11): [True: 537, False: 22.7k]
  ------------------
   61|    537|        return result(error_code::SURROGATE, pos);
   62|    537|      }
   63|  22.7k|      char16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (63:28): [True: 22.7k, Folded]
  ------------------
   64|  22.7k|                               ? u16_swap_bytes(data[pos + 1])
   65|  22.7k|                               : data[pos + 1];
   66|  22.7k|      char16_t diff2 = uint16_t(next_word - 0xDC00);
   67|  22.7k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (67:11): [True: 621, False: 22.1k]
  ------------------
   68|    621|        return result(error_code::SURROGATE, pos);
   69|    621|      }
   70|  22.1k|      pos += 2;
   71|  21.3M|    } else {
   72|  21.3M|      pos++;
   73|  21.3M|    }
   74|  21.4M|  }
   75|  2.49k|  return result(error_code::SUCCESS, pos);
   76|  3.88k|}
_ZN7simdutf6scalar5utf1617count_code_pointsILNS_10endiannessE0EEEmPKDsm:
   79|  5.00k|simdutf_constexpr23 size_t count_code_points(const char16_t *p, size_t len) {
   80|       |  // We are not BOM aware.
   81|  5.00k|  size_t counter{0};
   82|  30.0M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (82:22): [True: 30.0M, False: 5.00k]
  ------------------
   83|  30.0M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
   84|  30.0M|    counter += ((word & 0xFC00) != 0xDC00);
   85|  30.0M|  }
   86|  5.00k|  return counter;
   87|  5.00k|}
_ZN7simdutf6scalar5utf1617count_code_pointsILNS_10endiannessE1EEEmPKDsm:
   79|  4.69k|simdutf_constexpr23 size_t count_code_points(const char16_t *p, size_t len) {
   80|       |  // We are not BOM aware.
   81|  4.69k|  size_t counter{0};
   82|  28.3M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (82:22): [True: 28.3M, False: 4.69k]
  ------------------
   83|  28.3M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
   84|  28.3M|    counter += ((word & 0xFC00) != 0xDC00);
   85|  28.3M|  }
   86|  4.69k|  return counter;
   87|  4.69k|}
_ZN7simdutf6scalar5utf1622utf8_length_from_utf16ILNS_10endiannessE0EEEmPKDsm:
   91|  2.30k|                                                  size_t len) {
   92|       |  // We are not BOM aware.
   93|  2.30k|  size_t counter{0};
   94|  17.5M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (94:22): [True: 17.5M, False: 2.30k]
  ------------------
   95|  17.5M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
   96|  17.5M|    counter++; // ASCII
   97|  17.5M|    counter += static_cast<size_t>(
   98|  17.5M|        word >
   99|  17.5M|        0x7F); // non-ASCII is at least 2 bytes, surrogates are 2*2 == 4 bytes
  100|  17.5M|    counter += static_cast<size_t>((word > 0x7FF && word <= 0xD7FF) ||
  ------------------
  |  Branch (100:37): [True: 2.44M, False: 15.0M]
  |  Branch (100:53): [True: 2.17M, False: 275k]
  ------------------
  101|  15.3M|                                   (word >= 0xE000)); // three-byte
  ------------------
  |  Branch (101:36): [True: 221k, False: 15.1M]
  ------------------
  102|  17.5M|  }
  103|  2.30k|  return counter;
  104|  2.30k|}
_ZN7simdutf6scalar5utf1622utf8_length_from_utf16ILNS_10endiannessE1EEEmPKDsm:
   91|  2.22k|                                                  size_t len) {
   92|       |  // We are not BOM aware.
   93|  2.22k|  size_t counter{0};
   94|  17.6M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (94:22): [True: 17.6M, False: 2.22k]
  ------------------
   95|  17.6M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
   96|  17.6M|    counter++; // ASCII
   97|  17.6M|    counter += static_cast<size_t>(
   98|  17.6M|        word >
   99|  17.6M|        0x7F); // non-ASCII is at least 2 bytes, surrogates are 2*2 == 4 bytes
  100|  17.6M|    counter += static_cast<size_t>((word > 0x7FF && word <= 0xD7FF) ||
  ------------------
  |  Branch (100:37): [True: 3.40M, False: 14.2M]
  |  Branch (100:53): [True: 3.15M, False: 245k]
  ------------------
  101|  14.4M|                                   (word >= 0xE000)); // three-byte
  ------------------
  |  Branch (101:36): [True: 205k, False: 14.2M]
  ------------------
  102|  17.6M|  }
  103|  2.22k|  return counter;
  104|  2.22k|}
_ZN7simdutf6scalar5utf1623utf32_length_from_utf16ILNS_10endiannessE0EEEmPKDsm:
  108|    417|                                                   size_t len) {
  109|       |  // We are not BOM aware.
  110|    417|  size_t counter{0};
  111|  5.29M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (111:22): [True: 5.29M, False: 417]
  ------------------
  112|  5.29M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
  113|  5.29M|    counter += ((word & 0xFC00) != 0xDC00);
  114|  5.29M|  }
  115|    417|  return counter;
  116|    417|}
_ZN7simdutf6scalar5utf1623utf32_length_from_utf16ILNS_10endiannessE1EEEmPKDsm:
  108|    389|                                                   size_t len) {
  109|       |  // We are not BOM aware.
  110|    389|  size_t counter{0};
  111|  5.22M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (111:22): [True: 5.22M, False: 389]
  ------------------
  112|  5.22M|    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
  113|  5.22M|    counter += ((word & 0xFC00) != 0xDC00);
  114|  5.22M|  }
  115|    389|  return counter;
  116|    389|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115utf16_to_latin17convertILNS_10endiannessE0EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT0_Esr7simdutf6detailE26index_assignable_from_charIT1_EEEmS8_mS9_:
   17|    254|                                   OutputPtr latin_output) {
   18|    254|  if (len == 0) {
  ------------------
  |  Branch (18:7): [True: 6, False: 248]
  ------------------
   19|      6|    return 0;
   20|      6|  }
   21|    248|  size_t pos = 0;
   22|    248|  const auto latin_output_start = latin_output;
   23|    248|  uint16_t word = 0;
   24|    248|  uint16_t too_large = 0;
   25|       |
   26|  4.20M|  while (pos < len) {
  ------------------
  |  Branch (26:10): [True: 4.20M, False: 248]
  ------------------
   27|  4.20M|    word = !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (27:12): [Folded, False: 4.20M]
  ------------------
   28|  4.20M|    too_large |= word;
   29|  4.20M|    *latin_output++ = char(word & 0xFF);
   30|  4.20M|    pos++;
   31|  4.20M|  }
   32|    248|  if ((too_large & 0xFF00) != 0) {
  ------------------
  |  Branch (32:7): [True: 162, False: 86]
  ------------------
   33|    162|    return 0;
   34|    162|  }
   35|       |
   36|     86|  return latin_output - latin_output_start;
   37|    248|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115utf16_to_latin17convertILNS_10endiannessE1EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT0_Esr7simdutf6detailE26index_assignable_from_charIT1_EEEmS8_mS9_:
   17|    214|                                   OutputPtr latin_output) {
   18|    214|  if (len == 0) {
  ------------------
  |  Branch (18:7): [True: 6, False: 208]
  ------------------
   19|      6|    return 0;
   20|      6|  }
   21|    208|  size_t pos = 0;
   22|    208|  const auto latin_output_start = latin_output;
   23|    208|  uint16_t word = 0;
   24|    208|  uint16_t too_large = 0;
   25|       |
   26|  1.60M|  while (pos < len) {
  ------------------
  |  Branch (26:10): [True: 1.60M, False: 208]
  ------------------
   27|  1.60M|    word = !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (27:12): [True: 1.60M, Folded]
  ------------------
   28|  1.60M|    too_large |= word;
   29|  1.60M|    *latin_output++ = char(word & 0xFF);
   30|  1.60M|    pos++;
   31|  1.60M|  }
   32|    208|  if ((too_large & 0xFF00) != 0) {
  ------------------
  |  Branch (32:7): [True: 116, False: 92]
  ------------------
   33|    116|    return 0;
   34|    116|  }
   35|       |
   36|     92|  return latin_output - latin_output_start;
   37|    208|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115utf16_to_latin119convert_with_errorsILNS_10endiannessE0EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT0_Esr7simdutf6detailE26index_assignable_from_charIT1_EEENS_6resultES8_mS9_:
   45|    468|                                               OutputPtr latin_output) {
   46|    468|  if (len == 0) {
  ------------------
  |  Branch (46:7): [True: 4, False: 464]
  ------------------
   47|      4|    return result(error_code::SUCCESS, 0);
   48|      4|  }
   49|    464|  size_t pos = 0;
   50|    464|  auto start = latin_output;
   51|    464|  uint16_t word;
   52|       |
   53|   385k|  while (pos < len) {
  ------------------
  |  Branch (53:10): [True: 385k, False: 220]
  ------------------
   54|       |#if SIMDUTF_CPLUSPLUS23
   55|       |    if !consteval
   56|       |#endif
   57|   385k|    {
   58|   385k|      if (pos + 16 <= len) { // if it is safe to read 32 more bytes, check that
  ------------------
  |  Branch (58:11): [True: 383k, False: 1.56k]
  ------------------
   59|       |                             // they are Latin1
   60|   383k|        uint64_t v1, v2, v3, v4;
   61|   383k|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
   62|   383k|        ::memcpy(&v2, data + pos + 4, sizeof(uint64_t));
   63|   383k|        ::memcpy(&v3, data + pos + 8, sizeof(uint64_t));
   64|   383k|        ::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|   383k|        if (((v1 | v2 | v3 | v4) & 0xFF00FF00FF00FF00) == 0) {
  ------------------
  |  Branch (79:13): [True: 383k, False: 418]
  ------------------
   80|   383k|          size_t final_pos = pos + 16;
   81|  6.51M|          while (pos < final_pos) {
  ------------------
  |  Branch (81:18): [True: 6.13M, False: 383k]
  ------------------
   82|  6.13M|            *latin_output++ = !match_system(big_endian)
  ------------------
  |  Branch (82:31): [Folded, False: 6.13M]
  ------------------
   83|  6.13M|                                  ? char(u16_swap_bytes(data[pos]))
   84|  6.13M|                                  : char(data[pos]);
   85|  6.13M|            pos++;
   86|  6.13M|          }
   87|   383k|          continue;
   88|   383k|        }
   89|   383k|      }
   90|   385k|    }
   91|       |
   92|  1.98k|    word = !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (92:12): [Folded, False: 1.98k]
  ------------------
   93|  1.98k|    if ((word & 0xFF00) == 0) {
  ------------------
  |  Branch (93:9): [True: 1.73k, False: 244]
  ------------------
   94|  1.73k|      *latin_output++ = char(word & 0xFF);
   95|  1.73k|      pos++;
   96|  1.73k|    } else {
   97|    244|      return result(error_code::TOO_LARGE, pos);
   98|    244|    }
   99|  1.98k|  }
  100|    220|  return result(error_code::SUCCESS, latin_output - start);
  101|    464|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115utf16_to_latin119convert_with_errorsILNS_10endiannessE1EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT0_Esr7simdutf6detailE26index_assignable_from_charIT1_EEENS_6resultES8_mS9_:
   45|    430|                                               OutputPtr latin_output) {
   46|    430|  if (len == 0) {
  ------------------
  |  Branch (46:7): [True: 4, False: 426]
  ------------------
   47|      4|    return result(error_code::SUCCESS, 0);
   48|      4|  }
   49|    426|  size_t pos = 0;
   50|    426|  auto start = latin_output;
   51|    426|  uint16_t word;
   52|       |
   53|   401k|  while (pos < len) {
  ------------------
  |  Branch (53:10): [True: 400k, False: 180]
  ------------------
   54|       |#if SIMDUTF_CPLUSPLUS23
   55|       |    if !consteval
   56|       |#endif
   57|   400k|    {
   58|   400k|      if (pos + 16 <= len) { // if it is safe to read 32 more bytes, check that
  ------------------
  |  Branch (58:11): [True: 399k, False: 1.34k]
  ------------------
   59|       |                             // they are Latin1
   60|   399k|        uint64_t v1, v2, v3, v4;
   61|   399k|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
   62|   399k|        ::memcpy(&v2, data + pos + 4, sizeof(uint64_t));
   63|   399k|        ::memcpy(&v3, data + pos + 8, sizeof(uint64_t));
   64|   399k|        ::memcpy(&v4, data + pos + 12, sizeof(uint64_t));
   65|       |
   66|   399k|        if constexpr (!match_system(big_endian)) {
   67|   399k|          v1 = (v1 >> 8) | (v1 << (64 - 8));
   68|   399k|        }
   69|   399k|        if constexpr (!match_system(big_endian)) {
   70|   399k|          v2 = (v2 >> 8) | (v2 << (64 - 8));
   71|   399k|        }
   72|   399k|        if constexpr (!match_system(big_endian)) {
   73|   399k|          v3 = (v3 >> 8) | (v3 << (64 - 8));
   74|   399k|        }
   75|   399k|        if constexpr (!match_system(big_endian)) {
   76|   399k|          v4 = (v4 >> 8) | (v4 << (64 - 8));
   77|   399k|        }
   78|       |
   79|   399k|        if (((v1 | v2 | v3 | v4) & 0xFF00FF00FF00FF00) == 0) {
  ------------------
  |  Branch (79:13): [True: 399k, False: 362]
  ------------------
   80|   399k|          size_t final_pos = pos + 16;
   81|  6.78M|          while (pos < final_pos) {
  ------------------
  |  Branch (81:18): [True: 6.38M, False: 399k]
  ------------------
   82|  6.38M|            *latin_output++ = !match_system(big_endian)
  ------------------
  |  Branch (82:31): [True: 6.38M, Folded]
  ------------------
   83|  6.38M|                                  ? char(u16_swap_bytes(data[pos]))
   84|  6.38M|                                  : char(data[pos]);
   85|  6.38M|            pos++;
   86|  6.38M|          }
   87|   399k|          continue;
   88|   399k|        }
   89|   399k|      }
   90|   400k|    }
   91|       |
   92|  1.70k|    word = !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (92:12): [True: 1.70k, Folded]
  ------------------
   93|  1.70k|    if ((word & 0xFF00) == 0) {
  ------------------
  |  Branch (93:9): [True: 1.45k, False: 246]
  ------------------
   94|  1.45k|      *latin_output++ = char(word & 0xFF);
   95|  1.45k|      pos++;
   96|  1.45k|    } else {
   97|    246|      return result(error_code::TOO_LARGE, pos);
   98|    246|    }
   99|  1.70k|  }
  100|    180|  return result(error_code::SUCCESS, latin_output - start);
  101|    426|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf16_to_utf327convertILNS_10endiannessE0EEEmPKDsmPDi:
   11|    784|                                   char32_t *utf32_output) {
   12|    784|  size_t pos = 0;
   13|    784|  char32_t *start{utf32_output};
   14|  1.26M|  while (pos < len) {
  ------------------
  |  Branch (14:10): [True: 1.25M, False: 462]
  ------------------
   15|  1.25M|    uint16_t word =
   16|  1.25M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (16:9): [Folded, False: 1.25M]
  ------------------
   17|  1.25M|    if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (17:9): [True: 1.25M, False: 3.14k]
  ------------------
   18|       |      // No surrogate pair, extend 16-bit word to 32-bit word
   19|  1.25M|      *utf32_output++ = char32_t(word);
   20|  1.25M|      pos++;
   21|  1.25M|    } else {
   22|       |      // must be a surrogate pair
   23|  3.14k|      uint16_t diff = uint16_t(word - 0xD800);
   24|  3.14k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (24:11): [True: 102, False: 3.04k]
  ------------------
   25|    102|        return 0;
   26|    102|      }
   27|  3.04k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (27:11): [True: 78, False: 2.96k]
  ------------------
   28|     78|        return 0;
   29|     78|      } // minimal bound checking
   30|  2.96k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (30:28): [Folded, False: 2.96k]
  ------------------
   31|  2.96k|                               ? u16_swap_bytes(data[pos + 1])
   32|  2.96k|                               : data[pos + 1];
   33|  2.96k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   34|  2.96k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (34:11): [True: 142, False: 2.82k]
  ------------------
   35|    142|        return 0;
   36|    142|      }
   37|  2.82k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   38|  2.82k|      *utf32_output++ = char32_t(value);
   39|  2.82k|      pos += 2;
   40|  2.82k|    }
   41|  1.25M|  }
   42|    462|  return utf32_output - start;
   43|    784|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf16_to_utf327convertILNS_10endiannessE1EEEmPKDsmPDi:
   11|    720|                                   char32_t *utf32_output) {
   12|    720|  size_t pos = 0;
   13|    720|  char32_t *start{utf32_output};
   14|  1.20M|  while (pos < len) {
  ------------------
  |  Branch (14:10): [True: 1.20M, False: 434]
  ------------------
   15|  1.20M|    uint16_t word =
   16|  1.20M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (16:9): [True: 1.20M, Folded]
  ------------------
   17|  1.20M|    if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (17:9): [True: 1.20M, False: 1.73k]
  ------------------
   18|       |      // No surrogate pair, extend 16-bit word to 32-bit word
   19|  1.20M|      *utf32_output++ = char32_t(word);
   20|  1.20M|      pos++;
   21|  1.20M|    } else {
   22|       |      // must be a surrogate pair
   23|  1.73k|      uint16_t diff = uint16_t(word - 0xD800);
   24|  1.73k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (24:11): [True: 84, False: 1.65k]
  ------------------
   25|     84|        return 0;
   26|     84|      }
   27|  1.65k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (27:11): [True: 90, False: 1.56k]
  ------------------
   28|     90|        return 0;
   29|     90|      } // minimal bound checking
   30|  1.56k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (30:28): [True: 1.56k, Folded]
  ------------------
   31|  1.56k|                               ? u16_swap_bytes(data[pos + 1])
   32|  1.56k|                               : data[pos + 1];
   33|  1.56k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   34|  1.56k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (34:11): [True: 112, False: 1.44k]
  ------------------
   35|    112|        return 0;
   36|    112|      }
   37|  1.44k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   38|  1.44k|      *utf32_output++ = char32_t(value);
   39|  1.44k|      pos += 2;
   40|  1.44k|    }
   41|  1.20M|  }
   42|    434|  return utf32_output - start;
   43|    720|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf16_to_utf3219convert_with_errorsILNS_10endiannessE0EEENS_6resultEPKDsmPDi:
   47|    850|                                               char32_t *utf32_output) {
   48|    850|  size_t pos = 0;
   49|    850|  char32_t *start{utf32_output};
   50|  4.40M|  while (pos < len) {
  ------------------
  |  Branch (50:10): [True: 4.40M, False: 456]
  ------------------
   51|  4.40M|    uint16_t word =
   52|  4.40M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (52:9): [Folded, False: 4.40M]
  ------------------
   53|  4.40M|    if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (53:9): [True: 4.39M, False: 14.8k]
  ------------------
   54|       |      // No surrogate pair, extend 16-bit word to 32-bit word
   55|  4.39M|      *utf32_output++ = char32_t(word);
   56|  4.39M|      pos++;
   57|  4.39M|    } else {
   58|       |      // must be a surrogate pair
   59|  14.8k|      uint16_t diff = uint16_t(word - 0xD800);
   60|  14.8k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (60:11): [True: 108, False: 14.7k]
  ------------------
   61|    108|        return result(error_code::SURROGATE, pos);
   62|    108|      }
   63|  14.7k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (63:11): [True: 96, False: 14.6k]
  ------------------
   64|     96|        return result(error_code::SURROGATE, pos);
   65|     96|      } // minimal bound checking
   66|  14.6k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (66:28): [Folded, False: 14.6k]
  ------------------
   67|  14.6k|                               ? u16_swap_bytes(data[pos + 1])
   68|  14.6k|                               : data[pos + 1];
   69|  14.6k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   70|  14.6k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (70:11): [True: 190, False: 14.4k]
  ------------------
   71|    190|        return result(error_code::SURROGATE, pos);
   72|    190|      }
   73|  14.4k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   74|  14.4k|      *utf32_output++ = char32_t(value);
   75|  14.4k|      pos += 2;
   76|  14.4k|    }
   77|  4.40M|  }
   78|    456|  return result(error_code::SUCCESS, utf32_output - start);
   79|    850|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf16_to_utf3219convert_with_errorsILNS_10endiannessE1EEENS_6resultEPKDsmPDi:
   47|    842|                                               char32_t *utf32_output) {
   48|    842|  size_t pos = 0;
   49|    842|  char32_t *start{utf32_output};
   50|  3.28M|  while (pos < len) {
  ------------------
  |  Branch (50:10): [True: 3.28M, False: 450]
  ------------------
   51|  3.28M|    uint16_t word =
   52|  3.28M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (52:9): [True: 3.28M, Folded]
  ------------------
   53|  3.28M|    if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (53:9): [True: 3.27M, False: 6.87k]
  ------------------
   54|       |      // No surrogate pair, extend 16-bit word to 32-bit word
   55|  3.27M|      *utf32_output++ = char32_t(word);
   56|  3.27M|      pos++;
   57|  3.27M|    } else {
   58|       |      // must be a surrogate pair
   59|  6.87k|      uint16_t diff = uint16_t(word - 0xD800);
   60|  6.87k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (60:11): [True: 114, False: 6.76k]
  ------------------
   61|    114|        return result(error_code::SURROGATE, pos);
   62|    114|      }
   63|  6.76k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (63:11): [True: 120, False: 6.64k]
  ------------------
   64|    120|        return result(error_code::SURROGATE, pos);
   65|    120|      } // minimal bound checking
   66|  6.64k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (66:28): [True: 6.64k, Folded]
  ------------------
   67|  6.64k|                               ? u16_swap_bytes(data[pos + 1])
   68|  6.64k|                               : data[pos + 1];
   69|  6.64k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   70|  6.64k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (70:11): [True: 158, False: 6.48k]
  ------------------
   71|    158|        return result(error_code::SURROGATE, pos);
   72|    158|      }
   73|  6.48k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   74|  6.48k|      *utf32_output++ = char32_t(value);
   75|  6.48k|      pos += 2;
   76|  6.48k|    }
   77|  3.28M|  }
   78|    450|  return result(error_code::SUCCESS, utf32_output - start);
   79|    842|}

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

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf87convertILNS_10endiannessE0EPKDsPcQsr7simdutf6detailE18indexes_into_utf16IT0_EEEmS8_mT1_:
   15|  1.84k|                                   OutputPtr utf8_output) {
   16|  1.84k|  size_t pos = 0;
   17|  1.84k|  const auto start = utf8_output;
   18|  2.85M|  while (pos < len) {
  ------------------
  |  Branch (18:10): [True: 2.84M, False: 1.43k]
  ------------------
   19|       |#if SIMDUTF_CPLUSPLUS23
   20|       |    if !consteval
   21|       |#endif
   22|  2.84M|    {
   23|       |      // try to convert the next block of 8 bytes
   24|  2.84M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (24:11): [True: 2.84M, False: 3.21k]
  ------------------
   25|       |                            // they are ascii
   26|  2.84M|        uint64_t v;
   27|  2.84M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   28|       |        if constexpr (!match_system(big_endian)) {
   29|       |          v = (v >> 8) | (v << (64 - 8));
   30|       |        }
   31|  2.84M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (31:13): [True: 1.45M, False: 1.39M]
  ------------------
   32|  1.45M|          size_t final_pos = pos + 4;
   33|  7.25M|          while (pos < final_pos) {
  ------------------
  |  Branch (33:18): [True: 5.80M, False: 1.45M]
  ------------------
   34|  5.80M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (34:30): [Folded, False: 5.80M]
  ------------------
   35|  5.80M|                                 ? char(u16_swap_bytes(data[pos]))
   36|  5.80M|                                 : char(data[pos]);
   37|  5.80M|            pos++;
   38|  5.80M|          }
   39|  1.45M|          continue;
   40|  1.45M|        }
   41|  2.84M|      }
   42|  2.84M|    }
   43|  1.39M|    uint16_t word =
   44|  1.39M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (44:9): [Folded, False: 1.39M]
  ------------------
   45|  1.39M|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (45:9): [True: 114k, False: 1.28M]
  ------------------
   46|       |      // will generate one UTF-8 bytes
   47|   114k|      *utf8_output++ = char(word);
   48|   114k|      pos++;
   49|  1.28M|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (49:16): [True: 1.10M, False: 181k]
  ------------------
   50|       |      // will generate two UTF-8 bytes
   51|       |      // we have 0b110XXXXX 0b10XXXXXX
   52|  1.10M|      *utf8_output++ = char((word >> 6) | 0b11000000);
   53|  1.10M|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   54|  1.10M|      pos++;
   55|  1.10M|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (55:16): [True: 177k, False: 4.21k]
  ------------------
   56|       |      // will generate three UTF-8 bytes
   57|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
   58|   177k|      *utf8_output++ = char((word >> 12) | 0b11100000);
   59|   177k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   60|   177k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   61|   177k|      pos++;
   62|   177k|    } else {
   63|       |      // must be a surrogate pair
   64|  4.21k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (64:11): [True: 84, False: 4.12k]
  ------------------
   65|     84|        return 0;
   66|     84|      }
   67|  4.12k|      uint16_t diff = uint16_t(word - 0xD800);
   68|  4.12k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (68:11): [True: 136, False: 3.99k]
  ------------------
   69|    136|        return 0;
   70|    136|      }
   71|  3.99k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (71:28): [Folded, False: 3.99k]
  ------------------
   72|  3.99k|                               ? u16_swap_bytes(data[pos + 1])
   73|  3.99k|                               : data[pos + 1];
   74|  3.99k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   75|  3.99k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (75:11): [True: 186, False: 3.80k]
  ------------------
   76|    186|        return 0;
   77|    186|      }
   78|  3.80k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   79|       |      // will generate four UTF-8 bytes
   80|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
   81|  3.80k|      *utf8_output++ = char((value >> 18) | 0b11110000);
   82|  3.80k|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
   83|  3.80k|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
   84|  3.80k|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
   85|  3.80k|      pos += 2;
   86|  3.80k|    }
   87|  1.39M|  }
   88|  1.43k|  return utf8_output - start;
   89|  1.84k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf87convertILNS_10endiannessE1EPKDsPcQsr7simdutf6detailE18indexes_into_utf16IT0_EEEmS8_mT1_:
   15|  1.63k|                                   OutputPtr utf8_output) {
   16|  1.63k|  size_t pos = 0;
   17|  1.63k|  const auto start = utf8_output;
   18|  3.19M|  while (pos < len) {
  ------------------
  |  Branch (18:10): [True: 3.19M, False: 1.34k]
  ------------------
   19|       |#if SIMDUTF_CPLUSPLUS23
   20|       |    if !consteval
   21|       |#endif
   22|  3.19M|    {
   23|       |      // try to convert the next block of 8 bytes
   24|  3.19M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (24:11): [True: 3.18M, False: 2.79k]
  ------------------
   25|       |                            // they are ascii
   26|  3.18M|        uint64_t v;
   27|  3.18M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   28|  3.18M|        if constexpr (!match_system(big_endian)) {
   29|  3.18M|          v = (v >> 8) | (v << (64 - 8));
   30|  3.18M|        }
   31|  3.18M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (31:13): [True: 1.15M, False: 2.03M]
  ------------------
   32|  1.15M|          size_t final_pos = pos + 4;
   33|  5.78M|          while (pos < final_pos) {
  ------------------
  |  Branch (33:18): [True: 4.62M, False: 1.15M]
  ------------------
   34|  4.62M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (34:30): [True: 4.62M, Folded]
  ------------------
   35|  4.62M|                                 ? char(u16_swap_bytes(data[pos]))
   36|  4.62M|                                 : char(data[pos]);
   37|  4.62M|            pos++;
   38|  4.62M|          }
   39|  1.15M|          continue;
   40|  1.15M|        }
   41|  3.18M|      }
   42|  3.19M|    }
   43|  2.03M|    uint16_t word =
   44|  2.03M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (44:9): [True: 2.03M, Folded]
  ------------------
   45|  2.03M|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (45:9): [True: 116k, False: 1.91M]
  ------------------
   46|       |      // will generate one UTF-8 bytes
   47|   116k|      *utf8_output++ = char(word);
   48|   116k|      pos++;
   49|  1.91M|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (49:16): [True: 38.2k, False: 1.88M]
  ------------------
   50|       |      // will generate two UTF-8 bytes
   51|       |      // we have 0b110XXXXX 0b10XXXXXX
   52|  38.2k|      *utf8_output++ = char((word >> 6) | 0b11000000);
   53|  38.2k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   54|  38.2k|      pos++;
   55|  1.88M|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (55:16): [True: 1.87M, False: 3.54k]
  ------------------
   56|       |      // will generate three UTF-8 bytes
   57|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
   58|  1.87M|      *utf8_output++ = char((word >> 12) | 0b11100000);
   59|  1.87M|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   60|  1.87M|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   61|  1.87M|      pos++;
   62|  1.87M|    } else {
   63|       |      // must be a surrogate pair
   64|  3.54k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (64:11): [True: 48, False: 3.49k]
  ------------------
   65|     48|        return 0;
   66|     48|      }
   67|  3.49k|      uint16_t diff = uint16_t(word - 0xD800);
   68|  3.49k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (68:11): [True: 140, False: 3.35k]
  ------------------
   69|    140|        return 0;
   70|    140|      }
   71|  3.35k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (71:28): [True: 3.35k, Folded]
  ------------------
   72|  3.35k|                               ? u16_swap_bytes(data[pos + 1])
   73|  3.35k|                               : data[pos + 1];
   74|  3.35k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   75|  3.35k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (75:11): [True: 96, False: 3.25k]
  ------------------
   76|     96|        return 0;
   77|     96|      }
   78|  3.25k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   79|       |      // will generate four UTF-8 bytes
   80|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
   81|  3.25k|      *utf8_output++ = char((value >> 18) | 0b11110000);
   82|  3.25k|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
   83|  3.25k|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
   84|  3.25k|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
   85|  3.25k|      pos += 2;
   86|  3.25k|    }
   87|  2.03M|  }
   88|  1.34k|  return utf8_output - start;
   89|  1.63k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf819convert_with_errorsILNS_10endiannessE0ELb0EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT1_Esr7simdutf6detailE26index_assignable_from_charIT2_EEENS_11full_resultES8_mS9_m:
   99|  1.67k|                                                    size_t utf8_len = 0) {
  100|  1.67k|  if (check_output && utf8_len == 0) {
  ------------------
  |  Branch (100:7): [Folded, False: 1.67k]
  |  Branch (100:23): [True: 0, False: 0]
  ------------------
  101|      0|    return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, 0, 0);
  102|      0|  }
  103|       |
  104|  1.67k|  size_t pos = 0;
  105|  1.67k|  auto start = utf8_output;
  106|  1.67k|  auto end = utf8_output + utf8_len;
  107|       |
  108|  4.44M|  while (pos < len) {
  ------------------
  |  Branch (108:10): [True: 4.44M, False: 1.18k]
  ------------------
  109|       |#if SIMDUTF_CPLUSPLUS23
  110|       |    if !consteval
  111|       |#endif
  112|  4.44M|    {
  113|       |      // try to convert the next block of 8 bytes
  114|  4.44M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (114:11): [True: 4.44M, False: 2.63k]
  ------------------
  115|       |                            // they are ascii
  116|  4.44M|        uint64_t v;
  117|  4.44M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
  118|       |        if constexpr (!match_system(big_endian))
  119|       |          v = (v >> 8) | (v << (64 - 8));
  120|  4.44M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (120:13): [True: 1.14M, False: 3.29M]
  ------------------
  121|  1.14M|          size_t final_pos = pos + 4;
  122|  5.71M|          while (pos < final_pos) {
  ------------------
  |  Branch (122:18): [True: 4.57M, False: 1.14M]
  ------------------
  123|  4.57M|            if (check_output && size_t(end - utf8_output) < 1) {
  ------------------
  |  Branch (123:17): [Folded, False: 4.57M]
  |  Branch (123:33): [True: 0, False: 0]
  ------------------
  124|      0|              return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  125|      0|                                 utf8_output - start);
  126|      0|            }
  127|  4.57M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (127:30): [Folded, False: 4.57M]
  ------------------
  128|  4.57M|                                 ? char(u16_swap_bytes(data[pos]))
  129|  4.57M|                                 : char(data[pos]);
  130|  4.57M|            pos++;
  131|  4.57M|          }
  132|  1.14M|          continue;
  133|  1.14M|        }
  134|  4.44M|      }
  135|  4.44M|    }
  136|       |
  137|  3.30M|    uint16_t word =
  138|  3.30M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (138:9): [Folded, False: 3.30M]
  ------------------
  139|  3.30M|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (139:9): [True: 180k, False: 3.12M]
  ------------------
  140|       |      // will generate one UTF-8 bytes
  141|   180k|      if (check_output && size_t(end - utf8_output) < 1) {
  ------------------
  |  Branch (141:11): [Folded, False: 180k]
  |  Branch (141:27): [True: 0, False: 0]
  ------------------
  142|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  143|      0|                           utf8_output - start);
  144|      0|      }
  145|   180k|      *utf8_output++ = char(word);
  146|   180k|      pos++;
  147|  3.12M|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (147:16): [True: 1.45M, False: 1.66M]
  ------------------
  148|       |      // will generate two UTF-8 bytes
  149|       |      // we have 0b110XXXXX 0b10XXXXXX
  150|  1.45M|      if (check_output && size_t(end - utf8_output) < 2) {
  ------------------
  |  Branch (150:11): [Folded, False: 1.45M]
  |  Branch (150:27): [True: 0, False: 0]
  ------------------
  151|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  152|      0|                           utf8_output - start);
  153|      0|      }
  154|  1.45M|      *utf8_output++ = char((word >> 6) | 0b11000000);
  155|  1.45M|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  156|  1.45M|      pos++;
  157|       |
  158|  1.66M|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (158:16): [True: 1.66M, False: 2.88k]
  ------------------
  159|       |      // will generate three UTF-8 bytes
  160|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
  161|  1.66M|      if (check_output && size_t(end - utf8_output) < 3) {
  ------------------
  |  Branch (161:11): [Folded, False: 1.66M]
  |  Branch (161:27): [True: 0, False: 0]
  ------------------
  162|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  163|      0|                           utf8_output - start);
  164|      0|      }
  165|  1.66M|      *utf8_output++ = char((word >> 12) | 0b11100000);
  166|  1.66M|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  167|  1.66M|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  168|  1.66M|      pos++;
  169|  1.66M|    } else {
  170|       |
  171|  2.88k|      if (check_output && size_t(end - utf8_output) < 4) {
  ------------------
  |  Branch (171:11): [Folded, False: 2.88k]
  |  Branch (171:27): [True: 0, False: 0]
  ------------------
  172|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  173|      0|                           utf8_output - start);
  174|      0|      }
  175|       |      // must be a surrogate pair
  176|  2.88k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (176:11): [True: 60, False: 2.82k]
  ------------------
  177|     60|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  178|     60|      }
  179|  2.82k|      uint16_t diff = uint16_t(word - 0xD800);
  180|  2.82k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (180:11): [True: 138, False: 2.68k]
  ------------------
  181|    138|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  182|    138|      }
  183|  2.68k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (183:28): [Folded, False: 2.68k]
  ------------------
  184|  2.68k|                               ? u16_swap_bytes(data[pos + 1])
  185|  2.68k|                               : data[pos + 1];
  186|  2.68k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
  187|  2.68k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (187:11): [True: 300, False: 2.38k]
  ------------------
  188|    300|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  189|    300|      }
  190|  2.38k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
  191|       |      // will generate four UTF-8 bytes
  192|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
  193|  2.38k|      *utf8_output++ = char((value >> 18) | 0b11110000);
  194|  2.38k|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  195|  2.38k|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  196|  2.38k|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
  197|  2.38k|      pos += 2;
  198|  2.38k|    }
  199|  3.30M|  }
  200|  1.18k|  return full_result(error_code::SUCCESS, pos, utf8_output - start);
  201|  1.67k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf819convert_with_errorsILNS_10endiannessE1ELb0EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT1_Esr7simdutf6detailE26index_assignable_from_charIT2_EEENS_11full_resultES8_mS9_m:
   99|  1.76k|                                                    size_t utf8_len = 0) {
  100|  1.76k|  if (check_output && utf8_len == 0) {
  ------------------
  |  Branch (100:7): [Folded, False: 1.76k]
  |  Branch (100:23): [True: 0, False: 0]
  ------------------
  101|      0|    return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, 0, 0);
  102|      0|  }
  103|       |
  104|  1.76k|  size_t pos = 0;
  105|  1.76k|  auto start = utf8_output;
  106|  1.76k|  auto end = utf8_output + utf8_len;
  107|       |
  108|  4.81M|  while (pos < len) {
  ------------------
  |  Branch (108:10): [True: 4.80M, False: 1.25k]
  ------------------
  109|       |#if SIMDUTF_CPLUSPLUS23
  110|       |    if !consteval
  111|       |#endif
  112|  4.80M|    {
  113|       |      // try to convert the next block of 8 bytes
  114|  4.80M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (114:11): [True: 4.80M, False: 2.73k]
  ------------------
  115|       |                            // they are ascii
  116|  4.80M|        uint64_t v;
  117|  4.80M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
  118|       |        if constexpr (!match_system(big_endian))
  119|  4.80M|          v = (v >> 8) | (v << (64 - 8));
  120|  4.80M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (120:13): [True: 2.59M, False: 2.21M]
  ------------------
  121|  2.59M|          size_t final_pos = pos + 4;
  122|  12.9M|          while (pos < final_pos) {
  ------------------
  |  Branch (122:18): [True: 10.3M, False: 2.59M]
  ------------------
  123|  10.3M|            if (check_output && size_t(end - utf8_output) < 1) {
  ------------------
  |  Branch (123:17): [Folded, False: 10.3M]
  |  Branch (123:33): [True: 0, False: 0]
  ------------------
  124|      0|              return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  125|      0|                                 utf8_output - start);
  126|      0|            }
  127|  10.3M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (127:30): [True: 10.3M, Folded]
  ------------------
  128|  10.3M|                                 ? char(u16_swap_bytes(data[pos]))
  129|  10.3M|                                 : char(data[pos]);
  130|  10.3M|            pos++;
  131|  10.3M|          }
  132|  2.59M|          continue;
  133|  2.59M|        }
  134|  4.80M|      }
  135|  4.80M|    }
  136|       |
  137|  2.21M|    uint16_t word =
  138|  2.21M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (138:9): [True: 2.21M, Folded]
  ------------------
  139|  2.21M|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (139:9): [True: 749k, False: 1.46M]
  ------------------
  140|       |      // will generate one UTF-8 bytes
  141|   749k|      if (check_output && size_t(end - utf8_output) < 1) {
  ------------------
  |  Branch (141:11): [Folded, False: 749k]
  |  Branch (141:27): [True: 0, False: 0]
  ------------------
  142|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  143|      0|                           utf8_output - start);
  144|      0|      }
  145|   749k|      *utf8_output++ = char(word);
  146|   749k|      pos++;
  147|  1.46M|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (147:16): [True: 322k, False: 1.14M]
  ------------------
  148|       |      // will generate two UTF-8 bytes
  149|       |      // we have 0b110XXXXX 0b10XXXXXX
  150|   322k|      if (check_output && size_t(end - utf8_output) < 2) {
  ------------------
  |  Branch (150:11): [Folded, False: 322k]
  |  Branch (150:27): [True: 0, False: 0]
  ------------------
  151|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  152|      0|                           utf8_output - start);
  153|      0|      }
  154|   322k|      *utf8_output++ = char((word >> 6) | 0b11000000);
  155|   322k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  156|   322k|      pos++;
  157|       |
  158|  1.14M|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (158:16): [True: 1.11M, False: 27.0k]
  ------------------
  159|       |      // will generate three UTF-8 bytes
  160|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
  161|  1.11M|      if (check_output && size_t(end - utf8_output) < 3) {
  ------------------
  |  Branch (161:11): [Folded, False: 1.11M]
  |  Branch (161:27): [True: 0, False: 0]
  ------------------
  162|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  163|      0|                           utf8_output - start);
  164|      0|      }
  165|  1.11M|      *utf8_output++ = char((word >> 12) | 0b11100000);
  166|  1.11M|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  167|  1.11M|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  168|  1.11M|      pos++;
  169|  1.11M|    } else {
  170|       |
  171|  27.0k|      if (check_output && size_t(end - utf8_output) < 4) {
  ------------------
  |  Branch (171:11): [Folded, False: 27.0k]
  |  Branch (171:27): [True: 0, False: 0]
  ------------------
  172|      0|        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
  173|      0|                           utf8_output - start);
  174|      0|      }
  175|       |      // must be a surrogate pair
  176|  27.0k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (176:11): [True: 48, False: 27.0k]
  ------------------
  177|     48|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  178|     48|      }
  179|  27.0k|      uint16_t diff = uint16_t(word - 0xD800);
  180|  27.0k|      if (diff > 0x3FF) {
  ------------------
  |  Branch (180:11): [True: 166, False: 26.8k]
  ------------------
  181|    166|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  182|    166|      }
  183|  26.8k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (183:28): [True: 26.8k, Folded]
  ------------------
  184|  26.8k|                               ? u16_swap_bytes(data[pos + 1])
  185|  26.8k|                               : data[pos + 1];
  186|  26.8k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
  187|  26.8k|      if (diff2 > 0x3FF) {
  ------------------
  |  Branch (187:11): [True: 290, False: 26.5k]
  ------------------
  188|    290|        return full_result(error_code::SURROGATE, pos, utf8_output - start);
  189|    290|      }
  190|  26.5k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
  191|       |      // will generate four UTF-8 bytes
  192|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
  193|  26.5k|      *utf8_output++ = char((value >> 18) | 0b11110000);
  194|  26.5k|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  195|  26.5k|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  196|  26.5k|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
  197|  26.5k|      pos += 2;
  198|  26.5k|    }
  199|  2.21M|  }
  200|  1.25k|  return full_result(error_code::SUCCESS, pos, utf8_output - start);
  201|  1.76k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf813convert_validILNS_10endiannessE0EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT0_Esr7simdutf6detailE26index_assignable_from_charIT1_EEEmS8_mS9_:
   15|    428|                                         OutputPtr utf8_output) {
   16|    428|  size_t pos = 0;
   17|    428|  auto start = utf8_output;
   18|  2.91M|  while (pos < len) {
  ------------------
  |  Branch (18:10): [True: 2.91M, False: 428]
  ------------------
   19|       |#if SIMDUTF_CPLUSPLUS23
   20|       |    if !consteval
   21|       |#endif
   22|  2.91M|    {
   23|       |      // try to convert the next block of 4 ASCII characters
   24|  2.91M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (24:11): [True: 2.91M, False: 904]
  ------------------
   25|       |                            // they are ascii
   26|  2.91M|        uint64_t v;
   27|  2.91M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   28|       |        if constexpr (!match_system(big_endian)) {
   29|       |          v = (v >> 8) | (v << (64 - 8));
   30|       |        }
   31|  2.91M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (31:13): [True: 1.87M, False: 1.04M]
  ------------------
   32|  1.87M|          size_t final_pos = pos + 4;
   33|  9.36M|          while (pos < final_pos) {
  ------------------
  |  Branch (33:18): [True: 7.49M, False: 1.87M]
  ------------------
   34|  7.49M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (34:30): [Folded, False: 7.49M]
  ------------------
   35|  7.49M|                                 ? char(u16_swap_bytes(data[pos]))
   36|  7.49M|                                 : char(data[pos]);
   37|  7.49M|            pos++;
   38|  7.49M|          }
   39|  1.87M|          continue;
   40|  1.87M|        }
   41|  2.91M|      }
   42|  2.91M|    }
   43|       |
   44|  1.04M|    uint16_t word =
   45|  1.04M|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (45:9): [Folded, False: 1.04M]
  ------------------
   46|  1.04M|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (46:9): [True: 404k, False: 638k]
  ------------------
   47|       |      // will generate one UTF-8 bytes
   48|   404k|      *utf8_output++ = char(word);
   49|   404k|      pos++;
   50|   638k|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (50:16): [True: 278k, False: 359k]
  ------------------
   51|       |      // will generate two UTF-8 bytes
   52|       |      // we have 0b110XXXXX 0b10XXXXXX
   53|   278k|      *utf8_output++ = char((word >> 6) | 0b11000000);
   54|   278k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   55|   278k|      pos++;
   56|   359k|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (56:16): [True: 353k, False: 6.92k]
  ------------------
   57|       |      // will generate three UTF-8 bytes
   58|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
   59|   353k|      *utf8_output++ = char((word >> 12) | 0b11100000);
   60|   353k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   61|   353k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   62|   353k|      pos++;
   63|   353k|    } else {
   64|       |      // must be a surrogate pair
   65|  6.92k|      uint16_t diff = uint16_t(word - 0xD800);
   66|  6.92k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (66:11): [True: 0, False: 6.92k]
  ------------------
   67|      0|        return 0;
   68|      0|      } // minimal bound checking
   69|  6.92k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (69:28): [Folded, False: 6.92k]
  ------------------
   70|  6.92k|                               ? u16_swap_bytes(data[pos + 1])
   71|  6.92k|                               : data[pos + 1];
   72|  6.92k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   73|  6.92k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   74|       |      // will generate four UTF-8 bytes
   75|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
   76|  6.92k|      *utf8_output++ = char((value >> 18) | 0b11110000);
   77|  6.92k|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
   78|  6.92k|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
   79|  6.92k|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
   80|  6.92k|      pos += 2;
   81|  6.92k|    }
   82|  1.04M|  }
   83|    428|  return utf8_output - start;
   84|    428|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf16_to_utf813convert_validILNS_10endiannessE1EPKDsPcQaasr7simdutf6detailE18indexes_into_utf16IT0_Esr7simdutf6detailE26index_assignable_from_charIT1_EEEmS8_mS9_:
   15|    400|                                         OutputPtr utf8_output) {
   16|    400|  size_t pos = 0;
   17|    400|  auto start = utf8_output;
   18|  2.38M|  while (pos < len) {
  ------------------
  |  Branch (18:10): [True: 2.38M, False: 400]
  ------------------
   19|       |#if SIMDUTF_CPLUSPLUS23
   20|       |    if !consteval
   21|       |#endif
   22|  2.38M|    {
   23|       |      // try to convert the next block of 4 ASCII characters
   24|  2.38M|      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (24:11): [True: 2.38M, False: 774]
  ------------------
   25|       |                            // they are ascii
   26|  2.38M|        uint64_t v;
   27|  2.38M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   28|  2.38M|        if constexpr (!match_system(big_endian)) {
   29|  2.38M|          v = (v >> 8) | (v << (64 - 8));
   30|  2.38M|        }
   31|  2.38M|        if ((v & 0xFF80FF80FF80FF80) == 0) {
  ------------------
  |  Branch (31:13): [True: 1.51M, False: 868k]
  ------------------
   32|  1.51M|          size_t final_pos = pos + 4;
   33|  7.59M|          while (pos < final_pos) {
  ------------------
  |  Branch (33:18): [True: 6.07M, False: 1.51M]
  ------------------
   34|  6.07M|            *utf8_output++ = !match_system(big_endian)
  ------------------
  |  Branch (34:30): [True: 6.07M, Folded]
  ------------------
   35|  6.07M|                                 ? char(u16_swap_bytes(data[pos]))
   36|  6.07M|                                 : char(data[pos]);
   37|  6.07M|            pos++;
   38|  6.07M|          }
   39|  1.51M|          continue;
   40|  1.51M|        }
   41|  2.38M|      }
   42|  2.38M|    }
   43|       |
   44|   869k|    uint16_t word =
   45|   869k|        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
  ------------------
  |  Branch (45:9): [True: 869k, Folded]
  ------------------
   46|   869k|    if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (46:9): [True: 244k, False: 625k]
  ------------------
   47|       |      // will generate one UTF-8 bytes
   48|   244k|      *utf8_output++ = char(word);
   49|   244k|      pos++;
   50|   625k|    } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (50:16): [True: 73.8k, False: 551k]
  ------------------
   51|       |      // will generate two UTF-8 bytes
   52|       |      // we have 0b110XXXXX 0b10XXXXXX
   53|  73.8k|      *utf8_output++ = char((word >> 6) | 0b11000000);
   54|  73.8k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   55|  73.8k|      pos++;
   56|   551k|    } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (56:16): [True: 549k, False: 2.36k]
  ------------------
   57|       |      // will generate three UTF-8 bytes
   58|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
   59|   549k|      *utf8_output++ = char((word >> 12) | 0b11100000);
   60|   549k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   61|   549k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   62|   549k|      pos++;
   63|   549k|    } else {
   64|       |      // must be a surrogate pair
   65|  2.36k|      uint16_t diff = uint16_t(word - 0xD800);
   66|  2.36k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (66:11): [True: 0, False: 2.36k]
  ------------------
   67|      0|        return 0;
   68|      0|      } // minimal bound checking
   69|  2.36k|      uint16_t next_word = !match_system(big_endian)
  ------------------
  |  Branch (69:28): [True: 2.36k, Folded]
  ------------------
   70|  2.36k|                               ? u16_swap_bytes(data[pos + 1])
   71|  2.36k|                               : data[pos + 1];
   72|  2.36k|      uint16_t diff2 = uint16_t(next_word - 0xDC00);
   73|  2.36k|      uint32_t value = (diff << 10) + diff2 + 0x10000;
   74|       |      // will generate four UTF-8 bytes
   75|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
   76|  2.36k|      *utf8_output++ = char((value >> 18) | 0b11110000);
   77|  2.36k|      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
   78|  2.36k|      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
   79|  2.36k|      *utf8_output++ = char((value & 0b111111) | 0b10000000);
   80|  2.36k|      pos += 2;
   81|  2.36k|    }
   82|   869k|  }
   83|    400|  return utf8_output - start;
   84|    400|}

_ZN7simdutf6scalar5utf328validateEPKDim:
   25|  5.45k|                                                        size_t len) noexcept {
   26|  5.45k|  return validate(reinterpret_cast<const uint32_t *>(buf), len);
   27|  5.45k|}
_ZN7simdutf6scalar5utf3220validate_with_errorsEPKDim:
   49|  7.44k|validate_with_errors(const char32_t *buf, size_t len) noexcept {
   50|  7.44k|  return validate_with_errors(reinterpret_cast<const uint32_t *>(buf), len);
   51|  7.44k|}
_ZN7simdutf6scalar5utf3222utf8_length_from_utf32EPKDim:
   54|  2.65k|                                                         size_t len) {
   55|       |  // We are not BOM aware.
   56|  2.65k|  size_t counter{0};
   57|  2.65M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (57:22): [True: 2.65M, False: 2.65k]
  ------------------
   58|       |    // credit: @ttsugriy  for the vectorizable approach
   59|  2.65M|    counter++;                                     // ASCII
   60|  2.65M|    counter += static_cast<size_t>(p[i] > 0x7F);   // two-byte
   61|  2.65M|    counter += static_cast<size_t>(p[i] > 0x7FF);  // three-byte
   62|  2.65M|    counter += static_cast<size_t>(p[i] > 0xFFFF); // four-bytes
   63|  2.65M|  }
   64|  2.65k|  return counter;
   65|  2.65k|}
_ZN7simdutf6scalar5utf3223utf16_length_from_utf32EPKDim:
   68|  3.96k|utf16_length_from_utf32(const char32_t *p, size_t len) {
   69|       |  // We are not BOM aware.
   70|  3.96k|  size_t counter{0};
   71|  6.21M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (71:22): [True: 6.21M, False: 3.96k]
  ------------------
   72|  6.21M|    counter++;                                     // non-surrogate word
   73|  6.21M|    counter += static_cast<size_t>(p[i] > 0xFFFF); // surrogate pair
   74|  6.21M|  }
   75|  3.96k|  return counter;
   76|  3.96k|}
_ZN7simdutf6scalar5utf328validateIPKjQsr7simdutf6detailE19indexes_into_uint32IT_EEEbS5_m:
   13|  5.45k|                                                      size_t len) noexcept {
   14|  5.45k|  uint64_t pos = 0;
   15|  1.72M|  for (; pos < len; pos++) {
  ------------------
  |  Branch (15:10): [True: 1.71M, False: 2.46k]
  ------------------
   16|  1.71M|    uint32_t word = data[pos];
   17|  1.71M|    if (word > 0x10FFFF || (word >= 0xD800 && word <= 0xDFFF)) {
  ------------------
  |  Branch (17:9): [True: 2.50k, False: 1.71M]
  |  Branch (17:29): [True: 38.9k, False: 1.67M]
  |  Branch (17:47): [True: 482, False: 38.4k]
  ------------------
   18|  2.98k|      return false;
   19|  2.98k|    }
   20|  1.71M|  }
   21|  2.46k|  return true;
   22|  5.45k|}
_ZN7simdutf6scalar5utf3220validate_with_errorsIPKjQsr7simdutf6detailE19indexes_into_uint32IT_EEENS_6resultES5_m:
   34|  7.44k|validate_with_errors(InputPtr data, size_t len) noexcept {
   35|  7.44k|  size_t pos = 0;
   36|  1.72M|  for (; pos < len; pos++) {
  ------------------
  |  Branch (36:10): [True: 1.72M, False: 2.46k]
  ------------------
   37|  1.72M|    uint32_t word = data[pos];
   38|  1.72M|    if (word > 0x10FFFF) {
  ------------------
  |  Branch (38:9): [True: 4.08k, False: 1.72M]
  ------------------
   39|  4.08k|      return result(error_code::TOO_LARGE, pos);
   40|  4.08k|    }
   41|  1.72M|    if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (41:9): [True: 40.2k, False: 1.67M]
  |  Branch (41:27): [True: 891, False: 39.3k]
  ------------------
   42|    891|      return result(error_code::SURROGATE, pos);
   43|    891|    }
   44|  1.72M|  }
   45|  2.46k|  return result(error_code::SUCCESS, pos);
   46|  7.44k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115utf32_to_latin17convertEPKDimPc:
   10|    446|                                          char *latin1_output) {
   11|    446|  char *start = latin1_output;
   12|    446|  uint32_t utf32_char;
   13|    446|  size_t pos = 0;
   14|    446|  uint32_t too_large = 0;
   15|       |
   16|   544k|  while (pos < len) {
  ------------------
  |  Branch (16:10): [True: 544k, False: 446]
  ------------------
   17|   544k|    utf32_char = (uint32_t)data[pos];
   18|   544k|    too_large |= utf32_char;
   19|   544k|    *latin1_output++ = (char)(utf32_char & 0xFF);
   20|   544k|    pos++;
   21|   544k|  }
   22|    446|  if ((too_large & 0xFFFFFF00) != 0) {
  ------------------
  |  Branch (22:7): [True: 336, False: 110]
  ------------------
   23|    336|    return 0;
   24|    336|  }
   25|    110|  return latin1_output - start;
   26|    446|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_115utf32_to_latin119convert_with_errorsEPKDimPc:
   30|  1.17k|                                                      char *latin1_output) {
   31|  1.17k|  char *start{latin1_output};
   32|  1.17k|  size_t pos = 0;
   33|   225k|  while (pos < len) {
  ------------------
  |  Branch (33:10): [True: 225k, False: 86]
  ------------------
   34|       |#if SIMDUTF_CPLUSPLUS23
   35|       |    if !consteval
   36|       |#endif
   37|   225k|    {
   38|   225k|      if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (38:11): [True: 224k, False: 312]
  ------------------
   39|       |                            // they are Latin1
   40|   224k|        uint64_t v;
   41|   224k|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   42|   224k|        if ((v & 0xFFFFFF00FFFFFF00) == 0) {
  ------------------
  |  Branch (42:13): [True: 223k, False: 980]
  ------------------
   43|   223k|          *latin1_output++ = char(data[pos]);
   44|   223k|          *latin1_output++ = char(data[pos + 1]);
   45|   223k|          pos += 2;
   46|   223k|          continue;
   47|   223k|        }
   48|   224k|      }
   49|   225k|    }
   50|       |
   51|  1.29k|    uint32_t utf32_char = data[pos];
   52|  1.29k|    if ((utf32_char & 0xFFFFFF00) ==
  ------------------
  |  Branch (52:9): [True: 200, False: 1.09k]
  ------------------
   53|  1.29k|        0) { // Check if the character can be represented in Latin-1
   54|    200|      *latin1_output++ = (char)(utf32_char & 0xFF);
   55|    200|      pos++;
   56|  1.09k|    } else {
   57|  1.09k|      return result(error_code::TOO_LARGE, pos);
   58|  1.09k|    };
   59|    200|  }
   60|     86|  return result(error_code::SUCCESS, latin1_output - start);
   61|  1.17k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf32_to_utf167convertILNS_10endiannessE0EEEmPKDimPDs:
   11|  1.73k|                                   char16_t *utf16_output) {
   12|  1.73k|  size_t pos = 0;
   13|  1.73k|  char16_t *start{utf16_output};
   14|   668k|  while (pos < len) {
  ------------------
  |  Branch (14:10): [True: 667k, False: 912]
  ------------------
   15|   667k|    uint32_t word = data[pos];
   16|   667k|    if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (16:9): [True: 663k, False: 3.45k]
  ------------------
   17|   663k|      if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (17:11): [True: 1.81k, False: 661k]
  |  Branch (17:29): [True: 202, False: 1.61k]
  ------------------
   18|    202|        return 0;
   19|    202|      }
   20|       |      // will not generate a surrogate pair
   21|   663k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (21:25): [Folded, False: 663k]
  ------------------
   22|   663k|                            ? char16_t(u16_swap_bytes(uint16_t(word)))
   23|   663k|                            : char16_t(word);
   24|   663k|    } else {
   25|       |      // will generate a surrogate pair
   26|  3.45k|      if (word > 0x10FFFF) {
  ------------------
  |  Branch (26:11): [True: 620, False: 2.83k]
  ------------------
   27|    620|        return 0;
   28|    620|      }
   29|  2.83k|      word -= 0x10000;
   30|  2.83k|      uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
   31|  2.83k|      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|  2.83k|      *utf16_output++ = char16_t(high_surrogate);
   37|  2.83k|      *utf16_output++ = char16_t(low_surrogate);
   38|  2.83k|    }
   39|   666k|    pos++;
   40|   666k|  }
   41|    912|  return utf16_output - start;
   42|  1.73k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf32_to_utf167convertILNS_10endiannessE1EEEmPKDimPDs:
   11|  1.44k|                                   char16_t *utf16_output) {
   12|  1.44k|  size_t pos = 0;
   13|  1.44k|  char16_t *start{utf16_output};
   14|   185k|  while (pos < len) {
  ------------------
  |  Branch (14:10): [True: 184k, False: 654]
  ------------------
   15|   184k|    uint32_t word = data[pos];
   16|   184k|    if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (16:9): [True: 181k, False: 2.75k]
  ------------------
   17|   181k|      if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (17:11): [True: 1.47k, False: 180k]
  |  Branch (17:29): [True: 172, False: 1.30k]
  ------------------
   18|    172|        return 0;
   19|    172|      }
   20|       |      // will not generate a surrogate pair
   21|   181k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (21:25): [True: 181k, Folded]
  ------------------
   22|   181k|                            ? char16_t(u16_swap_bytes(uint16_t(word)))
   23|   181k|                            : char16_t(word);
   24|   181k|    } else {
   25|       |      // will generate a surrogate pair
   26|  2.75k|      if (word > 0x10FFFF) {
  ------------------
  |  Branch (26:11): [True: 616, False: 2.13k]
  ------------------
   27|    616|        return 0;
   28|    616|      }
   29|  2.13k|      word -= 0x10000;
   30|  2.13k|      uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
   31|  2.13k|      uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
   32|  2.13k|      if constexpr (!match_system(big_endian)) {
   33|  2.13k|        high_surrogate = u16_swap_bytes(high_surrogate);
   34|  2.13k|        low_surrogate = u16_swap_bytes(low_surrogate);
   35|  2.13k|      }
   36|  2.13k|      *utf16_output++ = char16_t(high_surrogate);
   37|  2.13k|      *utf16_output++ = char16_t(low_surrogate);
   38|  2.13k|    }
   39|   183k|    pos++;
   40|   183k|  }
   41|    654|  return utf16_output - start;
   42|  1.44k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf32_to_utf1619convert_with_errorsILNS_10endiannessE0EEENS_6resultEPKDimPDs:
   46|  1.94k|                                               char16_t *utf16_output) {
   47|  1.94k|  size_t pos = 0;
   48|  1.94k|  char16_t *start{utf16_output};
   49|   448k|  while (pos < len) {
  ------------------
  |  Branch (49:10): [True: 448k, False: 500]
  ------------------
   50|   448k|    uint32_t word = data[pos];
   51|   448k|    if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (51:9): [True: 438k, False: 10.0k]
  ------------------
   52|   438k|      if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (52:11): [True: 1.76k, False: 436k]
  |  Branch (52:29): [True: 300, False: 1.46k]
  ------------------
   53|    300|        return result(error_code::SURROGATE, pos);
   54|    300|      }
   55|       |      // will not generate a surrogate pair
   56|   437k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (56:25): [Folded, False: 437k]
  ------------------
   57|   437k|                            ? char16_t(u16_swap_bytes(uint16_t(word)))
   58|   437k|                            : char16_t(word);
   59|   437k|    } else {
   60|       |      // will generate a surrogate pair
   61|  10.0k|      if (word > 0x10FFFF) {
  ------------------
  |  Branch (61:11): [True: 1.14k, False: 8.88k]
  ------------------
   62|  1.14k|        return result(error_code::TOO_LARGE, pos);
   63|  1.14k|      }
   64|  8.88k|      word -= 0x10000;
   65|  8.88k|      uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
   66|  8.88k|      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|  8.88k|      *utf16_output++ = char16_t(high_surrogate);
   72|  8.88k|      *utf16_output++ = char16_t(low_surrogate);
   73|  8.88k|    }
   74|   446k|    pos++;
   75|   446k|  }
   76|    500|  return result(error_code::SUCCESS, utf16_output - start);
   77|  1.94k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf32_to_utf1619convert_with_errorsILNS_10endiannessE1EEENS_6resultEPKDimPDs:
   46|  1.75k|                                               char16_t *utf16_output) {
   47|  1.75k|  size_t pos = 0;
   48|  1.75k|  char16_t *start{utf16_output};
   49|  26.8k|  while (pos < len) {
  ------------------
  |  Branch (49:10): [True: 26.3k, False: 508]
  ------------------
   50|  26.3k|    uint32_t word = data[pos];
   51|  26.3k|    if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (51:9): [True: 22.6k, False: 3.71k]
  ------------------
   52|  22.6k|      if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (52:11): [True: 1.47k, False: 21.1k]
  |  Branch (52:29): [True: 210, False: 1.26k]
  ------------------
   53|    210|        return result(error_code::SURROGATE, pos);
   54|    210|      }
   55|       |      // will not generate a surrogate pair
   56|  22.4k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (56:25): [True: 22.4k, Folded]
  ------------------
   57|  22.4k|                            ? char16_t(u16_swap_bytes(uint16_t(word)))
   58|  22.4k|                            : char16_t(word);
   59|  22.4k|    } else {
   60|       |      // will generate a surrogate pair
   61|  3.71k|      if (word > 0x10FFFF) {
  ------------------
  |  Branch (61:11): [True: 1.03k, False: 2.68k]
  ------------------
   62|  1.03k|        return result(error_code::TOO_LARGE, pos);
   63|  1.03k|      }
   64|  2.68k|      word -= 0x10000;
   65|  2.68k|      uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
   66|  2.68k|      uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
   67|  2.68k|      if constexpr (!match_system(big_endian)) {
   68|  2.68k|        high_surrogate = u16_swap_bytes(high_surrogate);
   69|  2.68k|        low_surrogate = u16_swap_bytes(low_surrogate);
   70|  2.68k|      }
   71|  2.68k|      *utf16_output++ = char16_t(high_surrogate);
   72|  2.68k|      *utf16_output++ = char16_t(low_surrogate);
   73|  2.68k|    }
   74|  25.0k|    pos++;
   75|  25.0k|  }
   76|    508|  return result(error_code::SUCCESS, utf16_output - start);
   77|  1.75k|}

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

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf32_to_utf87convertIPKDiPcQaasr7simdutf6detailE18indexes_into_utf32IT_Esr7simdutf6detailE26index_assignable_from_charIT0_EEEmS7_mS8_:
   15|  1.82k|                                   OutputPtr utf8_output) {
   16|  1.82k|  size_t pos = 0;
   17|  1.82k|  auto start = utf8_output;
   18|   273k|  while (pos < len) {
  ------------------
  |  Branch (18:10): [True: 273k, False: 748]
  ------------------
   19|       |#if SIMDUTF_CPLUSPLUS23
   20|       |    if !consteval
   21|       |#endif
   22|   273k|    { // try to convert the next block of 2 ASCII characters
   23|   273k|      if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (23:11): [True: 272k, False: 1.02k]
  ------------------
   24|       |                            // they are ascii
   25|   272k|        uint64_t v;
   26|   272k|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   27|   272k|        if ((v & 0xFFFFFF80FFFFFF80) == 0) {
  ------------------
  |  Branch (27:13): [True: 252k, False: 19.0k]
  ------------------
   28|   252k|          *utf8_output++ = char(data[pos]);
   29|   252k|          *utf8_output++ = char(data[pos + 1]);
   30|   252k|          pos += 2;
   31|   252k|          continue;
   32|   252k|        }
   33|   272k|      }
   34|   273k|    }
   35|       |
   36|  20.0k|    uint32_t word = data[pos];
   37|  20.0k|    if ((word & 0xFFFFFF80) == 0) {
  ------------------
  |  Branch (37:9): [True: 2.56k, False: 17.5k]
  ------------------
   38|       |      // will generate one UTF-8 bytes
   39|  2.56k|      *utf8_output++ = char(word);
   40|  2.56k|      pos++;
   41|  17.5k|    } else if ((word & 0xFFFFF800) == 0) {
  ------------------
  |  Branch (41:16): [True: 3.20k, False: 14.3k]
  ------------------
   42|       |      // will generate two UTF-8 bytes
   43|       |      // we have 0b110XXXXX 0b10XXXXXX
   44|  3.20k|      *utf8_output++ = char((word >> 6) | 0b11000000);
   45|  3.20k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   46|  3.20k|      pos++;
   47|  14.3k|    } else if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (47:16): [True: 8.70k, False: 5.59k]
  ------------------
   48|       |      // will generate three UTF-8 bytes
   49|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
   50|  8.70k|      if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (50:11): [True: 6.25k, False: 2.45k]
  |  Branch (50:29): [True: 204, False: 6.04k]
  ------------------
   51|    204|        return 0;
   52|    204|      }
   53|  8.50k|      *utf8_output++ = char((word >> 12) | 0b11100000);
   54|  8.50k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   55|  8.50k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   56|  8.50k|      pos++;
   57|  8.50k|    } else {
   58|       |      // will generate four UTF-8 bytes
   59|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
   60|  5.59k|      if (word > 0x10FFFF) {
  ------------------
  |  Branch (60:11): [True: 870, False: 4.72k]
  ------------------
   61|    870|        return 0;
   62|    870|      }
   63|  4.72k|      *utf8_output++ = char((word >> 18) | 0b11110000);
   64|  4.72k|      *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
   65|  4.72k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
   66|  4.72k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
   67|  4.72k|      pos++;
   68|  4.72k|    }
   69|  20.0k|  }
   70|    748|  return utf8_output - start;
   71|  1.82k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf32_to_utf819convert_with_errorsIPKDiPcQaasr7simdutf6detailE18indexes_into_utf32IT_Esr7simdutf6detailE26index_assignable_from_charIT0_EEENS_6resultES7_mS8_:
   79|  2.66k|                                               OutputPtr utf8_output) {
   80|  2.66k|  size_t pos = 0;
   81|  2.66k|  auto start = utf8_output;
   82|   298k|  while (pos < len) {
  ------------------
  |  Branch (82:10): [True: 297k, False: 658]
  ------------------
   83|       |#if SIMDUTF_CPLUSPLUS23
   84|       |    if !consteval
   85|       |#endif
   86|   297k|    { // try to convert the next block of 2 ASCII characters
   87|   297k|      if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (87:11): [True: 296k, False: 1.01k]
  ------------------
   88|       |                            // they are ascii
   89|   296k|        uint64_t v;
   90|   296k|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   91|   296k|        if ((v & 0xFFFFFF80FFFFFF80) == 0) {
  ------------------
  |  Branch (91:13): [True: 238k, False: 58.4k]
  ------------------
   92|   238k|          *utf8_output++ = char(data[pos]);
   93|   238k|          *utf8_output++ = char(data[pos + 1]);
   94|   238k|          pos += 2;
   95|   238k|          continue;
   96|   238k|        }
   97|   296k|      }
   98|   297k|    }
   99|       |
  100|  59.4k|    uint32_t word = data[pos];
  101|  59.4k|    if ((word & 0xFFFFFF80) == 0) {
  ------------------
  |  Branch (101:9): [True: 8.06k, False: 51.4k]
  ------------------
  102|       |      // will generate one UTF-8 bytes
  103|  8.06k|      *utf8_output++ = char(word);
  104|  8.06k|      pos++;
  105|  51.4k|    } else if ((word & 0xFFFFF800) == 0) {
  ------------------
  |  Branch (105:16): [True: 25.9k, False: 25.4k]
  ------------------
  106|       |      // will generate two UTF-8 bytes
  107|       |      // we have 0b110XXXXX 0b10XXXXXX
  108|  25.9k|      *utf8_output++ = char((word >> 6) | 0b11000000);
  109|  25.9k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  110|  25.9k|      pos++;
  111|  25.9k|    } else if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (111:16): [True: 17.3k, False: 8.07k]
  ------------------
  112|       |      // will generate three UTF-8 bytes
  113|       |      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
  114|  17.3k|      if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (114:11): [True: 12.3k, False: 5.05k]
  |  Branch (114:29): [True: 336, False: 11.9k]
  ------------------
  115|    336|        return result(error_code::SURROGATE, pos);
  116|    336|      }
  117|  17.0k|      *utf8_output++ = char((word >> 12) | 0b11100000);
  118|  17.0k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  119|  17.0k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  120|  17.0k|      pos++;
  121|  17.0k|    } else {
  122|       |      // will generate four UTF-8 bytes
  123|       |      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
  124|  8.07k|      if (word > 0x10FFFF) {
  ------------------
  |  Branch (124:11): [True: 1.67k, False: 6.40k]
  ------------------
  125|  1.67k|        return result(error_code::TOO_LARGE, pos);
  126|  1.67k|      }
  127|  6.40k|      *utf8_output++ = char((word >> 18) | 0b11110000);
  128|  6.40k|      *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
  129|  6.40k|      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  130|  6.40k|      *utf8_output++ = char((word & 0b111111) | 0b10000000);
  131|  6.40k|      pos++;
  132|  6.40k|    }
  133|  59.4k|  }
  134|    658|  return result(error_code::SUCCESS, utf8_output - start);
  135|  2.66k|}

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

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf88validateEPKcm:
  111|  3.42k|                                                        size_t len) noexcept {
  112|  3.42k|  return validate(reinterpret_cast<const uint8_t *>(buf), len);
  113|  3.42k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf88validateIPKhEEbT_m:
   12|  3.42k|                                                      size_t len) noexcept {
   13|  3.42k|  static_assert(
   14|  3.42k|      std::is_same<typename std::decay<decltype(*data)>::type, uint8_t>::value,
   15|  3.42k|      "dereferencing the data pointer must result in a uint8_t");
   16|  3.42k|  uint64_t pos = 0;
   17|  3.42k|  uint32_t code_point = 0;
   18|  9.05M|  while (pos < len) {
  ------------------
  |  Branch (18:10): [True: 9.05M, False: 649]
  ------------------
   19|  9.05M|    uint64_t next_pos;
   20|       |#if SIMDUTF_CPLUSPLUS23
   21|       |    if !consteval
   22|       |#endif
   23|  9.05M|    { // check if the next 16 bytes are ascii.
   24|  9.05M|      next_pos = pos + 16;
   25|  9.05M|      if (next_pos <= len) { // if it is safe to read 16 more bytes, check
  ------------------
  |  Branch (25:11): [True: 9.05M, False: 3.38k]
  ------------------
   26|       |                             // that they are ascii
   27|  9.05M|        uint64_t v1{};
   28|  9.05M|        std::memcpy(&v1, data + pos, sizeof(uint64_t));
   29|  9.05M|        uint64_t v2{};
   30|  9.05M|        std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   31|  9.05M|        uint64_t v{v1 | v2};
   32|  9.05M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (32:13): [True: 7.83M, False: 1.21M]
  ------------------
   33|  7.83M|          pos = next_pos;
   34|  7.83M|          continue;
   35|  7.83M|        }
   36|  9.05M|      }
   37|  9.05M|    }
   38|       |
   39|  1.21M|    unsigned char byte = data[pos];
   40|       |
   41|  5.70M|    while (byte < 0b10000000) {
  ------------------
  |  Branch (41:12): [True: 4.48M, False: 1.21M]
  ------------------
   42|  4.48M|      if (++pos == len) {
  ------------------
  |  Branch (42:11): [True: 1.07k, False: 4.48M]
  ------------------
   43|  1.07k|        return true;
   44|  1.07k|      }
   45|  4.48M|      byte = data[pos];
   46|  4.48M|    }
   47|       |
   48|  1.21M|    if ((byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (48:9): [True: 849k, False: 366k]
  ------------------
   49|   849k|      next_pos = pos + 2;
   50|   849k|      if (next_pos > len) {
  ------------------
  |  Branch (50:11): [True: 60, False: 849k]
  ------------------
   51|     60|        return false;
   52|     60|      }
   53|   849k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (53:11): [True: 236, False: 848k]
  ------------------
   54|    236|        return false;
   55|    236|      }
   56|       |      // range check
   57|   848k|      code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
   58|   848k|      if (code_point < 0x80) {
  ------------------
  |  Branch (58:11): [True: 31, False: 848k]
  ------------------
   59|     31|        return false;
   60|     31|      }
   61|   848k|    } else if ((byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (61:16): [True: 278k, False: 88.2k]
  ------------------
   62|   278k|      next_pos = pos + 3;
   63|   278k|      if (next_pos > len) {
  ------------------
  |  Branch (63:11): [True: 48, False: 278k]
  ------------------
   64|     48|        return false;
   65|     48|      }
   66|   278k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (66:11): [True: 116, False: 277k]
  ------------------
   67|    116|        return false;
   68|    116|      }
   69|   277k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (69:11): [True: 61, False: 277k]
  ------------------
   70|     61|        return false;
   71|     61|      }
   72|       |      // range check
   73|   277k|      code_point = (byte & 0b00001111) << 12 |
   74|   277k|                   (data[pos + 1] & 0b00111111) << 6 |
   75|   277k|                   (data[pos + 2] & 0b00111111);
   76|   277k|      if ((code_point < 0x800) ||
  ------------------
  |  Branch (76:11): [True: 39, False: 277k]
  ------------------
   77|   277k|          (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (77:12): [True: 7.31k, False: 270k]
  |  Branch (77:35): [True: 18, False: 7.29k]
  ------------------
   78|     57|        return false;
   79|     57|      }
   80|   277k|    } else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (80:16): [True: 87.4k, False: 825]
  ------------------
   81|  87.4k|      next_pos = pos + 4;
   82|  87.4k|      if (next_pos > len) {
  ------------------
  |  Branch (82:11): [True: 44, False: 87.3k]
  ------------------
   83|     44|        return false;
   84|     44|      }
   85|  87.3k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (85:11): [True: 80, False: 87.2k]
  ------------------
   86|     80|        return false;
   87|     80|      }
   88|  87.2k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (88:11): [True: 39, False: 87.2k]
  ------------------
   89|     39|        return false;
   90|     39|      }
   91|  87.2k|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (91:11): [True: 40, False: 87.1k]
  ------------------
   92|     40|        return false;
   93|     40|      }
   94|       |      // range check
   95|  87.1k|      code_point =
   96|  87.1k|          (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
   97|  87.1k|          (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
   98|  87.1k|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (98:11): [True: 34, False: 87.1k]
  |  Branch (98:35): [True: 31, False: 87.1k]
  ------------------
   99|     65|        return false;
  100|     65|      }
  101|  87.1k|    } else {
  102|       |      // we may have a continuation
  103|    825|      return false;
  104|    825|    }
  105|  1.21M|    pos = next_pos;
  106|  1.21M|  }
  107|    649|  return true;
  108|  3.42k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf820validate_with_errorsEPKcm:
  219|  6.56k|validate_with_errors(const char *buf, size_t len) noexcept {
  220|  6.56k|  return validate_with_errors(reinterpret_cast<const uint8_t *>(buf), len);
  221|  6.56k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf820validate_with_errorsIPKhEENS_6resultET_m:
  117|  6.56k|validate_with_errors(BytePtr data, size_t len) noexcept {
  118|  6.56k|  static_assert(
  119|  6.56k|      std::is_same<typename std::decay<decltype(*data)>::type, uint8_t>::value,
  120|  6.56k|      "dereferencing the data pointer must result in a uint8_t");
  121|  6.56k|  size_t pos = 0;
  122|  6.56k|  uint32_t code_point = 0;
  123|  9.06M|  while (pos < len) {
  ------------------
  |  Branch (123:10): [True: 9.06M, False: 649]
  ------------------
  124|       |    // check of the next 16 bytes are ascii.
  125|  9.06M|    size_t next_pos = pos + 16;
  126|  9.06M|    if (next_pos <=
  ------------------
  |  Branch (126:9): [True: 9.05M, False: 5.06k]
  ------------------
  127|  9.06M|        len) { // if it is safe to read 16 more bytes, check that they are ascii
  128|  9.05M|      uint64_t v1;
  129|  9.05M|      std::memcpy(&v1, data + pos, sizeof(uint64_t));
  130|  9.05M|      uint64_t v2;
  131|  9.05M|      std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  132|  9.05M|      uint64_t v{v1 | v2};
  133|  9.05M|      if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (133:11): [True: 7.83M, False: 1.21M]
  ------------------
  134|  7.83M|        pos = next_pos;
  135|  7.83M|        continue;
  136|  7.83M|      }
  137|  9.05M|    }
  138|  1.22M|    unsigned char byte = data[pos];
  139|       |
  140|  5.72M|    while (byte < 0b10000000) {
  ------------------
  |  Branch (140:12): [True: 4.50M, False: 1.22M]
  ------------------
  141|  4.50M|      if (++pos == len) {
  ------------------
  |  Branch (141:11): [True: 1.07k, False: 4.49M]
  ------------------
  142|  1.07k|        return result(error_code::SUCCESS, len);
  143|  1.07k|      }
  144|  4.49M|      byte = data[pos];
  145|  4.49M|    }
  146|       |
  147|  1.22M|    if ((byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (147:9): [True: 851k, False: 370k]
  ------------------
  148|   851k|      next_pos = pos + 2;
  149|   851k|      if (next_pos > len) {
  ------------------
  |  Branch (149:11): [True: 180, False: 851k]
  ------------------
  150|    180|        return result(error_code::TOO_SHORT, pos);
  151|    180|      }
  152|   851k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (152:11): [True: 708, False: 850k]
  ------------------
  153|    708|        return result(error_code::TOO_SHORT, pos);
  154|    708|      }
  155|       |      // range check
  156|   850k|      code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
  157|   850k|      if (code_point < 0x80) {
  ------------------
  |  Branch (157:11): [True: 93, False: 850k]
  ------------------
  158|     93|        return result(error_code::OVERLONG, pos);
  159|     93|      }
  160|   850k|    } else if ((byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (160:16): [True: 279k, False: 90.5k]
  ------------------
  161|   279k|      next_pos = pos + 3;
  162|   279k|      if (next_pos > len) {
  ------------------
  |  Branch (162:11): [True: 144, False: 279k]
  ------------------
  163|    144|        return result(error_code::TOO_SHORT, pos);
  164|    144|      }
  165|   279k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (165:11): [True: 348, False: 279k]
  ------------------
  166|    348|        return result(error_code::TOO_SHORT, pos);
  167|    348|      }
  168|   279k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (168:11): [True: 183, False: 279k]
  ------------------
  169|    183|        return result(error_code::TOO_SHORT, pos);
  170|    183|      }
  171|       |      // range check
  172|   279k|      code_point = (byte & 0b00001111) << 12 |
  173|   279k|                   (data[pos + 1] & 0b00111111) << 6 |
  174|   279k|                   (data[pos + 2] & 0b00111111);
  175|   279k|      if (code_point < 0x800) {
  ------------------
  |  Branch (175:11): [True: 117, False: 278k]
  ------------------
  176|    117|        return result(error_code::OVERLONG, pos);
  177|    117|      }
  178|   278k|      if (0xd7ff < code_point && code_point < 0xe000) {
  ------------------
  |  Branch (178:11): [True: 8.03k, False: 270k]
  |  Branch (178:34): [True: 54, False: 7.97k]
  ------------------
  179|     54|        return result(error_code::SURROGATE, pos);
  180|     54|      }
  181|   278k|    } else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (181:16): [True: 88.3k, False: 2.21k]
  ------------------
  182|  88.3k|      next_pos = pos + 4;
  183|  88.3k|      if (next_pos > len) {
  ------------------
  |  Branch (183:11): [True: 132, False: 88.2k]
  ------------------
  184|    132|        return result(error_code::TOO_SHORT, pos);
  185|    132|      }
  186|  88.2k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (186:11): [True: 240, False: 88.0k]
  ------------------
  187|    240|        return result(error_code::TOO_SHORT, pos);
  188|    240|      }
  189|  88.0k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (189:11): [True: 117, False: 87.8k]
  ------------------
  190|    117|        return result(error_code::TOO_SHORT, pos);
  191|    117|      }
  192|  87.8k|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (192:11): [True: 120, False: 87.7k]
  ------------------
  193|    120|        return result(error_code::TOO_SHORT, pos);
  194|    120|      }
  195|       |      // range check
  196|  87.7k|      code_point =
  197|  87.7k|          (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
  198|  87.7k|          (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
  199|  87.7k|      if (code_point <= 0xffff) {
  ------------------
  |  Branch (199:11): [True: 102, False: 87.6k]
  ------------------
  200|    102|        return result(error_code::OVERLONG, pos);
  201|    102|      }
  202|  87.6k|      if (0x10ffff < code_point) {
  ------------------
  |  Branch (202:11): [True: 93, False: 87.5k]
  ------------------
  203|     93|        return result(error_code::TOO_LARGE, pos);
  204|     93|      }
  205|  87.6k|    } else {
  206|       |      // we either have too many continuation bytes or an invalid leading byte
  207|  2.21k|      if ((byte & 0b11000000) == 0b10000000) {
  ------------------
  |  Branch (207:11): [True: 1.15k, False: 1.05k]
  ------------------
  208|  1.15k|        return result(error_code::TOO_LONG, pos);
  209|  1.15k|      } else {
  210|  1.05k|        return result(error_code::HEADER_BITS, pos);
  211|  1.05k|      }
  212|  2.21k|    }
  213|  1.21M|    pos = next_pos;
  214|  1.21M|  }
  215|    649|  return result(error_code::SUCCESS, len);
  216|  6.56k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf831rewind_and_validate_with_errorsEPKcS4_m:
  230|  3.40k|    const char *start, const char *buf, size_t len) noexcept {
  231|       |  // First check that we start with a leading byte
  232|  3.40k|  if ((*start & 0b11000000) == 0b10000000) {
  ------------------
  |  Branch (232:7): [True: 260, False: 3.14k]
  ------------------
  233|    260|    return result(error_code::TOO_LONG, 0);
  234|    260|  }
  235|  3.14k|  size_t extra_len{0};
  236|       |  // A leading byte cannot be further than 4 bytes away
  237|  3.35k|  for (int i = 0; i < 5; i++) {
  ------------------
  |  Branch (237:19): [True: 3.35k, False: 0]
  ------------------
  238|  3.35k|    unsigned char byte = *buf;
  239|  3.35k|    if ((byte & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (239:9): [True: 3.14k, False: 212]
  ------------------
  240|  3.14k|      break;
  241|  3.14k|    } else {
  242|    212|      buf--;
  243|    212|      extra_len++;
  244|    212|    }
  245|  3.35k|  }
  246|       |
  247|  3.14k|  result res = validate_with_errors(buf, len + extra_len);
  248|  3.14k|  res.count -= extra_len; // Might underflow
  249|  3.14k|  return res;
  250|  3.40k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf817count_code_pointsIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEEmS6_m:
  256|  14.8k|simdutf_constexpr23 size_t count_code_points(InputPtr data, size_t len) {
  257|  14.8k|  size_t counter{0};
  258|   275M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (258:22): [True: 275M, False: 14.8k]
  ------------------
  259|       |    // -65 is 0b10111111, anything larger in two-complement's should start a new
  260|       |    // code point.
  261|   275M|    if (int8_t(data[i]) > -65) {
  ------------------
  |  Branch (261:9): [True: 272M, False: 3.30M]
  ------------------
  262|   272M|      counter++;
  263|   272M|    }
  264|   275M|  }
  265|  14.8k|  return counter;
  266|  14.8k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_14utf822utf16_length_from_utf8IPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEEmS6_m:
  272|  5.66k|simdutf_constexpr23 size_t utf16_length_from_utf8(InputPtr data, size_t len) {
  273|  5.66k|  size_t counter{0};
  274|  96.9M|  for (size_t i = 0; i < len; i++) {
  ------------------
  |  Branch (274:22): [True: 96.9M, False: 5.66k]
  ------------------
  275|  96.9M|    if (int8_t(data[i]) > -65) {
  ------------------
  |  Branch (275:9): [True: 95.3M, False: 1.60M]
  ------------------
  276|  95.3M|      counter++;
  277|  95.3M|    }
  278|  96.9M|    if (uint8_t(data[i]) >= 240) {
  ------------------
  |  Branch (278:9): [True: 158k, False: 96.7M]
  ------------------
  279|   158k|      counter++;
  280|   158k|    }
  281|  96.9M|  }
  282|  5.66k|  return counter;
  283|  5.66k|}

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

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf167convertILNS_10endiannessE0EPKcQsr7simdutf6detailE22indexes_into_byte_likeIT0_EEEmS7_mPDs:
   14|  1.62k|                                   char16_t *utf16_output) {
   15|  1.62k|  size_t pos = 0;
   16|  1.62k|  char16_t *start{utf16_output};
   17|  1.71M|  while (pos < len) {
  ------------------
  |  Branch (17:10): [True: 1.71M, False: 700]
  ------------------
   18|       |#if SIMDUTF_CPLUSPLUS23
   19|       |    if !consteval
   20|       |#endif
   21|       |    // try to convert the next block of 16 ASCII bytes
   22|  1.71M|    {
   23|  1.71M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (23:11): [True: 1.71M, False: 4.73k]
  ------------------
   24|       |                             // they are ascii
   25|  1.71M|        uint64_t v1;
   26|  1.71M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
   27|  1.71M|        uint64_t v2;
   28|  1.71M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   29|  1.71M|        uint64_t v{v1 | v2};
   30|  1.71M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (30:13): [True: 1.07M, False: 643k]
  ------------------
   31|  1.07M|          size_t final_pos = pos + 16;
   32|  18.1M|          while (pos < final_pos) {
  ------------------
  |  Branch (32:18): [True: 17.1M, False: 1.07M]
  ------------------
   33|  17.1M|            *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (33:31): [Folded, False: 17.1M]
  ------------------
   34|  17.1M|                                  ? char16_t(u16_swap_bytes(data[pos]))
   35|  17.1M|                                  : char16_t(data[pos]);
   36|  17.1M|            pos++;
   37|  17.1M|          }
   38|  1.07M|          continue;
   39|  1.07M|        }
   40|  1.71M|      }
   41|  1.71M|    }
   42|       |
   43|   647k|    uint8_t leading_byte = data[pos]; // leading byte
   44|   647k|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (44:9): [True: 511k, False: 136k]
  ------------------
   45|       |      // converting one ASCII byte !!!
   46|   511k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (46:25): [Folded, False: 511k]
  ------------------
   47|   511k|                            ? char16_t(u16_swap_bytes(leading_byte))
   48|   511k|                            : char16_t(leading_byte);
   49|   511k|      pos++;
   50|   511k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (50:16): [True: 89.6k, False: 46.4k]
  ------------------
   51|       |      // We have a two-byte UTF-8, it should become
   52|       |      // a single UTF-16 word.
   53|  89.6k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (53:11): [True: 18, False: 89.5k]
  ------------------
   54|     18|        return 0;
   55|     18|      } // minimal bound checking
   56|  89.5k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (56:11): [True: 94, False: 89.5k]
  ------------------
   57|     94|        return 0;
   58|     94|      }
   59|       |      // range check
   60|  89.5k|      uint32_t code_point =
   61|  89.5k|          (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
   62|  89.5k|      if (code_point < 0x80) {
  ------------------
  |  Branch (62:11): [True: 16, False: 89.4k]
  ------------------
   63|     16|        return 0;
   64|     16|      }
   65|       |      if constexpr (!match_system(big_endian)) {
   66|       |        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
   67|       |      }
   68|  89.4k|      *utf16_output++ = char16_t(code_point);
   69|  89.4k|      pos += 2;
   70|  89.4k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (70:16): [True: 38.0k, False: 8.38k]
  ------------------
   71|       |      // We have a three-byte UTF-8, it should become
   72|       |      // a single UTF-16 word.
   73|  38.0k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (73:11): [True: 60, False: 37.9k]
  ------------------
   74|     60|        return 0;
   75|     60|      } // minimal bound checking
   76|       |
   77|  37.9k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (77:11): [True: 54, False: 37.9k]
  ------------------
   78|     54|        return 0;
   79|     54|      }
   80|  37.9k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (80:11): [True: 38, False: 37.9k]
  ------------------
   81|     38|        return 0;
   82|     38|      }
   83|       |      // range check
   84|  37.9k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
   85|  37.9k|                            (data[pos + 1] & 0b00111111) << 6 |
   86|  37.9k|                            (data[pos + 2] & 0b00111111);
   87|  37.9k|      if (code_point < 0x800 || (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (87:11): [True: 38, False: 37.8k]
  |  Branch (87:34): [True: 928, False: 36.9k]
  |  Branch (87:57): [True: 16, False: 912]
  ------------------
   88|     54|        return 0;
   89|     54|      }
   90|       |      if constexpr (!match_system(big_endian)) {
   91|       |        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
   92|       |      }
   93|  37.8k|      *utf16_output++ = char16_t(code_point);
   94|  37.8k|      pos += 3;
   95|  37.8k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (95:16): [True: 7.96k, False: 416]
  ------------------
   96|       |      // we have a 4-byte UTF-8 word.
   97|  7.96k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (97:11): [True: 42, False: 7.92k]
  ------------------
   98|     42|        return 0;
   99|     42|      } // minimal bound checking
  100|  7.92k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (100:11): [True: 28, False: 7.89k]
  ------------------
  101|     28|        return 0;
  102|     28|      }
  103|  7.89k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (103:11): [True: 26, False: 7.87k]
  ------------------
  104|     26|        return 0;
  105|     26|      }
  106|  7.87k|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (106:11): [True: 34, False: 7.83k]
  ------------------
  107|     34|        return 0;
  108|     34|      }
  109|       |
  110|       |      // range check
  111|  7.83k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  112|  7.83k|                            (data[pos + 1] & 0b00111111) << 12 |
  113|  7.83k|                            (data[pos + 2] & 0b00111111) << 6 |
  114|  7.83k|                            (data[pos + 3] & 0b00111111);
  115|  7.83k|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (115:11): [True: 18, False: 7.81k]
  |  Branch (115:35): [True: 28, False: 7.79k]
  ------------------
  116|     46|        return 0;
  117|     46|      }
  118|  7.79k|      code_point -= 0x10000;
  119|  7.79k|      uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10));
  120|  7.79k|      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
  121|       |      if constexpr (!match_system(big_endian)) {
  122|       |        high_surrogate = u16_swap_bytes(high_surrogate);
  123|       |        low_surrogate = u16_swap_bytes(low_surrogate);
  124|       |      }
  125|  7.79k|      *utf16_output++ = char16_t(high_surrogate);
  126|  7.79k|      *utf16_output++ = char16_t(low_surrogate);
  127|  7.79k|      pos += 4;
  128|  7.79k|    } else {
  129|    416|      return 0;
  130|    416|    }
  131|   647k|  }
  132|    700|  return utf16_output - start;
  133|  1.62k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf167convertILNS_10endiannessE1EPKcQsr7simdutf6detailE22indexes_into_byte_likeIT0_EEEmS7_mPDs:
   14|  1.50k|                                   char16_t *utf16_output) {
   15|  1.50k|  size_t pos = 0;
   16|  1.50k|  char16_t *start{utf16_output};
   17|  1.41M|  while (pos < len) {
  ------------------
  |  Branch (17:10): [True: 1.41M, False: 572]
  ------------------
   18|       |#if SIMDUTF_CPLUSPLUS23
   19|       |    if !consteval
   20|       |#endif
   21|       |    // try to convert the next block of 16 ASCII bytes
   22|  1.41M|    {
   23|  1.41M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (23:11): [True: 1.41M, False: 3.78k]
  ------------------
   24|       |                             // they are ascii
   25|  1.41M|        uint64_t v1;
   26|  1.41M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
   27|  1.41M|        uint64_t v2;
   28|  1.41M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   29|  1.41M|        uint64_t v{v1 | v2};
   30|  1.41M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (30:13): [True: 845k, False: 569k]
  ------------------
   31|   845k|          size_t final_pos = pos + 16;
   32|  14.3M|          while (pos < final_pos) {
  ------------------
  |  Branch (32:18): [True: 13.5M, False: 845k]
  ------------------
   33|  13.5M|            *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (33:31): [True: 13.5M, Folded]
  ------------------
   34|  13.5M|                                  ? char16_t(u16_swap_bytes(data[pos]))
   35|  13.5M|                                  : char16_t(data[pos]);
   36|  13.5M|            pos++;
   37|  13.5M|          }
   38|   845k|          continue;
   39|   845k|        }
   40|  1.41M|      }
   41|  1.41M|    }
   42|       |
   43|   572k|    uint8_t leading_byte = data[pos]; // leading byte
   44|   572k|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (44:9): [True: 465k, False: 107k]
  ------------------
   45|       |      // converting one ASCII byte !!!
   46|   465k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (46:25): [True: 465k, Folded]
  ------------------
   47|   465k|                            ? char16_t(u16_swap_bytes(leading_byte))
   48|   465k|                            : char16_t(leading_byte);
   49|   465k|      pos++;
   50|   465k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (50:16): [True: 69.6k, False: 38.2k]
  ------------------
   51|       |      // We have a two-byte UTF-8, it should become
   52|       |      // a single UTF-16 word.
   53|  69.6k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (53:11): [True: 42, False: 69.6k]
  ------------------
   54|     42|        return 0;
   55|     42|      } // minimal bound checking
   56|  69.6k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (56:11): [True: 92, False: 69.5k]
  ------------------
   57|     92|        return 0;
   58|     92|      }
   59|       |      // range check
   60|  69.5k|      uint32_t code_point =
   61|  69.5k|          (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
   62|  69.5k|      if (code_point < 0x80) {
  ------------------
  |  Branch (62:11): [True: 12, False: 69.5k]
  ------------------
   63|     12|        return 0;
   64|     12|      }
   65|  69.5k|      if constexpr (!match_system(big_endian)) {
   66|  69.5k|        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
   67|  69.5k|      }
   68|  69.5k|      *utf16_output++ = char16_t(code_point);
   69|  69.5k|      pos += 2;
   70|  69.5k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (70:16): [True: 29.4k, False: 8.78k]
  ------------------
   71|       |      // We have a three-byte UTF-8, it should become
   72|       |      // a single UTF-16 word.
   73|  29.4k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (73:11): [True: 24, False: 29.4k]
  ------------------
   74|     24|        return 0;
   75|     24|      } // minimal bound checking
   76|       |
   77|  29.4k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (77:11): [True: 62, False: 29.3k]
  ------------------
   78|     62|        return 0;
   79|     62|      }
   80|  29.3k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (80:11): [True: 26, False: 29.3k]
  ------------------
   81|     26|        return 0;
   82|     26|      }
   83|       |      // range check
   84|  29.3k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
   85|  29.3k|                            (data[pos + 1] & 0b00111111) << 6 |
   86|  29.3k|                            (data[pos + 2] & 0b00111111);
   87|  29.3k|      if (code_point < 0x800 || (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (87:11): [True: 42, False: 29.3k]
  |  Branch (87:34): [True: 1.03k, False: 28.2k]
  |  Branch (87:57): [True: 16, False: 1.01k]
  ------------------
   88|     58|        return 0;
   89|     58|      }
   90|  29.3k|      if constexpr (!match_system(big_endian)) {
   91|  29.3k|        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
   92|  29.3k|      }
   93|  29.3k|      *utf16_output++ = char16_t(code_point);
   94|  29.3k|      pos += 3;
   95|  29.3k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (95:16): [True: 8.33k, False: 448]
  ------------------
   96|       |      // we have a 4-byte UTF-8 word.
   97|  8.33k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (97:11): [True: 24, False: 8.31k]
  ------------------
   98|     24|        return 0;
   99|     24|      } // minimal bound checking
  100|  8.31k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (100:11): [True: 42, False: 8.27k]
  ------------------
  101|     42|        return 0;
  102|     42|      }
  103|  8.27k|      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (103:11): [True: 18, False: 8.25k]
  ------------------
  104|     18|        return 0;
  105|     18|      }
  106|  8.25k|      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (106:11): [True: 32, False: 8.22k]
  ------------------
  107|     32|        return 0;
  108|     32|      }
  109|       |
  110|       |      // range check
  111|  8.22k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  112|  8.22k|                            (data[pos + 1] & 0b00111111) << 12 |
  113|  8.22k|                            (data[pos + 2] & 0b00111111) << 6 |
  114|  8.22k|                            (data[pos + 3] & 0b00111111);
  115|  8.22k|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (115:11): [True: 42, False: 8.18k]
  |  Branch (115:35): [True: 12, False: 8.16k]
  ------------------
  116|     54|        return 0;
  117|     54|      }
  118|  8.16k|      code_point -= 0x10000;
  119|  8.16k|      uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10));
  120|  8.16k|      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
  121|  8.16k|      if constexpr (!match_system(big_endian)) {
  122|  8.16k|        high_surrogate = u16_swap_bytes(high_surrogate);
  123|  8.16k|        low_surrogate = u16_swap_bytes(low_surrogate);
  124|  8.16k|      }
  125|  8.16k|      *utf16_output++ = char16_t(high_surrogate);
  126|  8.16k|      *utf16_output++ = char16_t(low_surrogate);
  127|  8.16k|      pos += 4;
  128|  8.16k|    } else {
  129|    448|      return 0;
  130|    448|    }
  131|   572k|  }
  132|    572|  return utf16_output - start;
  133|  1.50k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf1619convert_with_errorsILNS_10endiannessE0EPKcQsr7simdutf6detailE22indexes_into_byte_likeIT0_EEENS_6resultES7_mPDs:
  140|  1.62k|                                               char16_t *utf16_output) {
  141|  1.62k|  size_t pos = 0;
  142|  1.62k|  char16_t *start{utf16_output};
  143|  2.86M|  while (pos < len) {
  ------------------
  |  Branch (143:10): [True: 2.86M, False: 748]
  ------------------
  144|       |#if SIMDUTF_CPLUSPLUS23
  145|       |    if !consteval
  146|       |#endif
  147|  2.86M|    {
  148|       |      // try to convert the next block of 16 ASCII bytes
  149|  2.86M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (149:11): [True: 2.86M, False: 5.78k]
  ------------------
  150|       |                             // they are ascii
  151|  2.86M|        uint64_t v1;
  152|  2.86M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
  153|  2.86M|        uint64_t v2;
  154|  2.86M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  155|  2.86M|        uint64_t v{v1 | v2};
  156|  2.86M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (156:13): [True: 1.50M, False: 1.35M]
  ------------------
  157|  1.50M|          size_t final_pos = pos + 16;
  158|  25.5M|          while (pos < final_pos) {
  ------------------
  |  Branch (158:18): [True: 24.0M, False: 1.50M]
  ------------------
  159|  24.0M|            const char16_t byte = uint8_t(data[pos]);
  160|  24.0M|            *utf16_output++ =
  161|  24.0M|                !match_system(big_endian) ? u16_swap_bytes(byte) : byte;
  ------------------
  |  Branch (161:17): [Folded, False: 24.0M]
  ------------------
  162|  24.0M|            pos++;
  163|  24.0M|          }
  164|  1.50M|          continue;
  165|  1.50M|        }
  166|  2.86M|      }
  167|  2.86M|    }
  168|       |
  169|  1.36M|    auto leading_byte = uint8_t(data[pos]); // leading byte
  170|  1.36M|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (170:9): [True: 1.11M, False: 245k]
  ------------------
  171|       |      // converting one ASCII byte !!!
  172|  1.11M|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (172:25): [Folded, False: 1.11M]
  ------------------
  173|  1.11M|                            ? char16_t(u16_swap_bytes(leading_byte))
  174|  1.11M|                            : char16_t(leading_byte);
  175|  1.11M|      pos++;
  176|  1.11M|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (176:16): [True: 167k, False: 78.3k]
  ------------------
  177|       |      // We have a two-byte UTF-8, it should become
  178|       |      // a single UTF-16 word.
  179|   167k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (179:11): [True: 42, False: 167k]
  ------------------
  180|     42|        return result(error_code::TOO_SHORT, pos);
  181|     42|      } // minimal bound checking
  182|   167k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (182:11): [True: 84, False: 166k]
  ------------------
  183|     84|        return result(error_code::TOO_SHORT, pos);
  184|     84|      }
  185|       |      // range check
  186|   166k|      uint32_t code_point = (leading_byte & 0b00011111) << 6 |
  187|   166k|                            (uint8_t(data[pos + 1]) & 0b00111111);
  188|   166k|      if (code_point < 0x80) {
  ------------------
  |  Branch (188:11): [True: 18, False: 166k]
  ------------------
  189|     18|        return result(error_code::OVERLONG, pos);
  190|     18|      }
  191|       |      if constexpr (!match_system(big_endian)) {
  192|       |        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
  193|       |      }
  194|   166k|      *utf16_output++ = char16_t(code_point);
  195|   166k|      pos += 2;
  196|   166k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (196:16): [True: 57.4k, False: 20.9k]
  ------------------
  197|       |      // We have a three-byte UTF-8, it should become
  198|       |      // a single UTF-16 word.
  199|  57.4k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (199:11): [True: 12, False: 57.4k]
  ------------------
  200|     12|        return result(error_code::TOO_SHORT, pos);
  201|     12|      } // minimal bound checking
  202|       |
  203|  57.4k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (203:11): [True: 60, False: 57.3k]
  ------------------
  204|     60|        return result(error_code::TOO_SHORT, pos);
  205|     60|      }
  206|  57.3k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (206:11): [True: 24, False: 57.3k]
  ------------------
  207|     24|        return result(error_code::TOO_SHORT, pos);
  208|     24|      }
  209|       |      // range check
  210|  57.3k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
  211|  57.3k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 6 |
  212|  57.3k|                            (uint8_t(data[pos + 2]) & 0b00111111);
  213|  57.3k|      if (code_point < 0x800) {
  ------------------
  |  Branch (213:11): [True: 36, False: 57.3k]
  ------------------
  214|     36|        return result(error_code::OVERLONG, pos);
  215|     36|      }
  216|  57.3k|      if (0xd7ff < code_point && code_point < 0xe000) {
  ------------------
  |  Branch (216:11): [True: 1.49k, False: 55.8k]
  |  Branch (216:34): [True: 6, False: 1.48k]
  ------------------
  217|      6|        return result(error_code::SURROGATE, pos);
  218|      6|      }
  219|       |      if constexpr (!match_system(big_endian)) {
  220|       |        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
  221|       |      }
  222|  57.3k|      *utf16_output++ = char16_t(code_point);
  223|  57.3k|      pos += 3;
  224|  57.3k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (224:16): [True: 20.5k, False: 384]
  ------------------
  225|       |      // we have a 4-byte UTF-8 word.
  226|  20.5k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (226:11): [True: 54, False: 20.4k]
  ------------------
  227|     54|        return result(error_code::TOO_SHORT, pos);
  228|     54|      } // minimal bound checking
  229|  20.4k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (229:11): [True: 72, False: 20.4k]
  ------------------
  230|     72|        return result(error_code::TOO_SHORT, pos);
  231|     72|      }
  232|  20.4k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (232:11): [True: 6, False: 20.4k]
  ------------------
  233|      6|        return result(error_code::TOO_SHORT, pos);
  234|      6|      }
  235|  20.4k|      if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (235:11): [True: 18, False: 20.3k]
  ------------------
  236|     18|        return result(error_code::TOO_SHORT, pos);
  237|     18|      }
  238|       |
  239|       |      // range check
  240|  20.3k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  241|  20.3k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 12 |
  242|  20.3k|                            (uint8_t(data[pos + 2]) & 0b00111111) << 6 |
  243|  20.3k|                            (uint8_t(data[pos + 3]) & 0b00111111);
  244|  20.3k|      if (code_point <= 0xffff) {
  ------------------
  |  Branch (244:11): [True: 36, False: 20.3k]
  ------------------
  245|     36|        return result(error_code::OVERLONG, pos);
  246|     36|      }
  247|  20.3k|      if (0x10ffff < code_point) {
  ------------------
  |  Branch (247:11): [True: 24, False: 20.3k]
  ------------------
  248|     24|        return result(error_code::TOO_LARGE, pos);
  249|     24|      }
  250|  20.3k|      code_point -= 0x10000;
  251|  20.3k|      uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10));
  252|  20.3k|      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
  253|       |      if constexpr (!match_system(big_endian)) {
  254|       |        high_surrogate = u16_swap_bytes(high_surrogate);
  255|       |        low_surrogate = u16_swap_bytes(low_surrogate);
  256|       |      }
  257|  20.3k|      *utf16_output++ = char16_t(high_surrogate);
  258|  20.3k|      *utf16_output++ = char16_t(low_surrogate);
  259|  20.3k|      pos += 4;
  260|  20.3k|    } else {
  261|       |      // we either have too many continuation bytes or an invalid leading byte
  262|    384|      if ((leading_byte & 0b11000000) == 0b10000000) {
  ------------------
  |  Branch (262:11): [True: 204, False: 180]
  ------------------
  263|    204|        return result(error_code::TOO_LONG, pos);
  264|    204|      } else {
  265|    180|        return result(error_code::HEADER_BITS, pos);
  266|    180|      }
  267|    384|    }
  268|  1.36M|  }
  269|    748|  return result(error_code::SUCCESS, utf16_output - start);
  270|  1.62k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf1619convert_with_errorsILNS_10endiannessE1EPKcQsr7simdutf6detailE22indexes_into_byte_likeIT0_EEENS_6resultES7_mPDs:
  140|  2.18k|                                               char16_t *utf16_output) {
  141|  2.18k|  size_t pos = 0;
  142|  2.18k|  char16_t *start{utf16_output};
  143|  3.38M|  while (pos < len) {
  ------------------
  |  Branch (143:10): [True: 3.38M, False: 1.00k]
  ------------------
  144|       |#if SIMDUTF_CPLUSPLUS23
  145|       |    if !consteval
  146|       |#endif
  147|  3.38M|    {
  148|       |      // try to convert the next block of 16 ASCII bytes
  149|  3.38M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (149:11): [True: 3.37M, False: 9.04k]
  ------------------
  150|       |                             // they are ascii
  151|  3.37M|        uint64_t v1;
  152|  3.37M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
  153|  3.37M|        uint64_t v2;
  154|  3.37M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  155|  3.37M|        uint64_t v{v1 | v2};
  156|  3.37M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (156:13): [True: 1.80M, False: 1.57M]
  ------------------
  157|  1.80M|          size_t final_pos = pos + 16;
  158|  30.6M|          while (pos < final_pos) {
  ------------------
  |  Branch (158:18): [True: 28.8M, False: 1.80M]
  ------------------
  159|  28.8M|            const char16_t byte = uint8_t(data[pos]);
  160|  28.8M|            *utf16_output++ =
  161|  28.8M|                !match_system(big_endian) ? u16_swap_bytes(byte) : byte;
  ------------------
  |  Branch (161:17): [True: 28.8M, Folded]
  ------------------
  162|  28.8M|            pos++;
  163|  28.8M|          }
  164|  1.80M|          continue;
  165|  1.80M|        }
  166|  3.37M|      }
  167|  3.38M|    }
  168|       |
  169|  1.57M|    auto leading_byte = uint8_t(data[pos]); // leading byte
  170|  1.57M|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (170:9): [True: 1.21M, False: 363k]
  ------------------
  171|       |      // converting one ASCII byte !!!
  172|  1.21M|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (172:25): [True: 1.21M, Folded]
  ------------------
  173|  1.21M|                            ? char16_t(u16_swap_bytes(leading_byte))
  174|  1.21M|                            : char16_t(leading_byte);
  175|  1.21M|      pos++;
  176|  1.21M|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (176:16): [True: 254k, False: 108k]
  ------------------
  177|       |      // We have a two-byte UTF-8, it should become
  178|       |      // a single UTF-16 word.
  179|   254k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (179:11): [True: 42, False: 254k]
  ------------------
  180|     42|        return result(error_code::TOO_SHORT, pos);
  181|     42|      } // minimal bound checking
  182|   254k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (182:11): [True: 150, False: 254k]
  ------------------
  183|    150|        return result(error_code::TOO_SHORT, pos);
  184|    150|      }
  185|       |      // range check
  186|   254k|      uint32_t code_point = (leading_byte & 0b00011111) << 6 |
  187|   254k|                            (uint8_t(data[pos + 1]) & 0b00111111);
  188|   254k|      if (code_point < 0x80) {
  ------------------
  |  Branch (188:11): [True: 18, False: 254k]
  ------------------
  189|     18|        return result(error_code::OVERLONG, pos);
  190|     18|      }
  191|   254k|      if constexpr (!match_system(big_endian)) {
  192|   254k|        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
  193|   254k|      }
  194|   254k|      *utf16_output++ = char16_t(code_point);
  195|   254k|      pos += 2;
  196|   254k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (196:16): [True: 80.5k, False: 28.2k]
  ------------------
  197|       |      // We have a three-byte UTF-8, it should become
  198|       |      // a single UTF-16 word.
  199|  80.5k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (199:11): [True: 42, False: 80.4k]
  ------------------
  200|     42|        return result(error_code::TOO_SHORT, pos);
  201|     42|      } // minimal bound checking
  202|       |
  203|  80.4k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (203:11): [True: 90, False: 80.3k]
  ------------------
  204|     90|        return result(error_code::TOO_SHORT, pos);
  205|     90|      }
  206|  80.3k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (206:11): [True: 66, False: 80.3k]
  ------------------
  207|     66|        return result(error_code::TOO_SHORT, pos);
  208|     66|      }
  209|       |      // range check
  210|  80.3k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
  211|  80.3k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 6 |
  212|  80.3k|                            (uint8_t(data[pos + 2]) & 0b00111111);
  213|  80.3k|      if (code_point < 0x800) {
  ------------------
  |  Branch (213:11): [True: 36, False: 80.2k]
  ------------------
  214|     36|        return result(error_code::OVERLONG, pos);
  215|     36|      }
  216|  80.2k|      if (0xd7ff < code_point && code_point < 0xe000) {
  ------------------
  |  Branch (216:11): [True: 3.04k, False: 77.2k]
  |  Branch (216:34): [True: 12, False: 3.03k]
  ------------------
  217|     12|        return result(error_code::SURROGATE, pos);
  218|     12|      }
  219|  80.2k|      if constexpr (!match_system(big_endian)) {
  220|  80.2k|        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
  221|  80.2k|      }
  222|  80.2k|      *utf16_output++ = char16_t(code_point);
  223|  80.2k|      pos += 3;
  224|  80.2k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (224:16): [True: 27.7k, False: 480]
  ------------------
  225|       |      // we have a 4-byte UTF-8 word.
  226|  27.7k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (226:11): [True: 60, False: 27.6k]
  ------------------
  227|     60|        return result(error_code::TOO_SHORT, pos);
  228|     60|      } // minimal bound checking
  229|  27.6k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (229:11): [True: 48, False: 27.6k]
  ------------------
  230|     48|        return result(error_code::TOO_SHORT, pos);
  231|     48|      }
  232|  27.6k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (232:11): [True: 42, False: 27.5k]
  ------------------
  233|     42|        return result(error_code::TOO_SHORT, pos);
  234|     42|      }
  235|  27.5k|      if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (235:11): [True: 48, False: 27.5k]
  ------------------
  236|     48|        return result(error_code::TOO_SHORT, pos);
  237|     48|      }
  238|       |
  239|       |      // range check
  240|  27.5k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  241|  27.5k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 12 |
  242|  27.5k|                            (uint8_t(data[pos + 2]) & 0b00111111) << 6 |
  243|  27.5k|                            (uint8_t(data[pos + 3]) & 0b00111111);
  244|  27.5k|      if (code_point <= 0xffff) {
  ------------------
  |  Branch (244:11): [True: 18, False: 27.5k]
  ------------------
  245|     18|        return result(error_code::OVERLONG, pos);
  246|     18|      }
  247|  27.5k|      if (0x10ffff < code_point) {
  ------------------
  |  Branch (247:11): [True: 24, False: 27.4k]
  ------------------
  248|     24|        return result(error_code::TOO_LARGE, pos);
  249|     24|      }
  250|  27.4k|      code_point -= 0x10000;
  251|  27.4k|      uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10));
  252|  27.4k|      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
  253|  27.4k|      if constexpr (!match_system(big_endian)) {
  254|  27.4k|        high_surrogate = u16_swap_bytes(high_surrogate);
  255|  27.4k|        low_surrogate = u16_swap_bytes(low_surrogate);
  256|  27.4k|      }
  257|  27.4k|      *utf16_output++ = char16_t(high_surrogate);
  258|  27.4k|      *utf16_output++ = char16_t(low_surrogate);
  259|  27.4k|      pos += 4;
  260|  27.4k|    } else {
  261|       |      // we either have too many continuation bytes or an invalid leading byte
  262|    480|      if ((leading_byte & 0b11000000) == 0b10000000) {
  ------------------
  |  Branch (262:11): [True: 180, False: 300]
  ------------------
  263|    180|        return result(error_code::TOO_LONG, pos);
  264|    300|      } else {
  265|    300|        return result(error_code::HEADER_BITS, pos);
  266|    300|      }
  267|    480|    }
  268|  1.57M|  }
  269|  1.00k|  return result(error_code::SUCCESS, utf16_output - start);
  270|  2.18k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf1630rewind_and_convert_with_errorsILNS_10endiannessE0EEENS_6resultEmPKcmPDs:
  290|  1.12k|                                             char16_t *utf16_output) {
  291|  1.12k|  size_t extra_len{0};
  292|       |  // We potentially need to go back in time and find a leading byte.
  293|       |  // In theory '3' would be sufficient, but sometimes the error can go back
  294|       |  // quite far.
  295|  1.12k|  size_t how_far_back = prior_bytes;
  296|       |  // size_t how_far_back = 3; // 3 bytes in the past + current position
  297|       |  // if(how_far_back >= prior_bytes) { how_far_back = prior_bytes; }
  298|  1.12k|  bool found_leading_bytes{false};
  299|       |  // important: it is i <= how_far_back and not 'i < how_far_back'.
  300|  1.20k|  for (size_t i = 0; i <= how_far_back; i++) {
  ------------------
  |  Branch (300:22): [True: 1.13k, False: 64]
  ------------------
  301|  1.13k|    unsigned char byte = buf[-static_cast<std::ptrdiff_t>(i)];
  302|  1.13k|    found_leading_bytes = ((byte & 0b11000000) != 0b10000000);
  303|  1.13k|    if (found_leading_bytes) {
  ------------------
  |  Branch (303:9): [True: 1.06k, False: 72]
  ------------------
  304|  1.06k|      if (i > 0 && byte < 128) {
  ------------------
  |  Branch (304:11): [True: 8, False: 1.05k]
  |  Branch (304:20): [True: 8, False: 0]
  ------------------
  305|       |        // If we had to go back and the leading byte is ascii
  306|       |        // then we can stop right away.
  307|      8|        return result(error_code::TOO_LONG, 0 - i + 1);
  308|      8|      }
  309|  1.05k|      buf -= i;
  310|  1.05k|      extra_len = i;
  311|  1.05k|      break;
  312|  1.06k|    }
  313|  1.13k|  }
  314|       |  //
  315|       |  // It is possible for this function to return a negative count in its result.
  316|       |  // C++ Standard Section 18.1 defines size_t is in <cstddef> which is described
  317|       |  // in C Standard as <stddef.h>. C Standard Section 4.1.5 defines size_t as an
  318|       |  // unsigned integral type of the result of the sizeof operator
  319|       |  //
  320|       |  // An unsigned type will simply wrap round arithmetically (well defined).
  321|       |  //
  322|  1.12k|  if (!found_leading_bytes) {
  ------------------
  |  Branch (322:7): [True: 64, False: 1.05k]
  ------------------
  323|       |    // If how_far_back == 3, we may have four consecutive continuation bytes!!!
  324|       |    // [....] [continuation] [continuation] [continuation] | [buf is
  325|       |    // continuation] Or we possibly have a stream that does not start with a
  326|       |    // leading byte.
  327|     64|    return result(error_code::TOO_LONG, 0 - how_far_back);
  328|     64|  }
  329|  1.05k|  result res = convert_with_errors<endian>(buf, len + extra_len, utf16_output);
  330|  1.05k|  if (res.error) {
  ------------------
  |  Branch (330:7): [True: 560, False: 496]
  ------------------
  331|    560|    res.count -= extra_len;
  332|    560|  }
  333|  1.05k|  return res;
  334|  1.12k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf1630rewind_and_convert_with_errorsILNS_10endiannessE1EEENS_6resultEmPKcmPDs:
  290|  1.50k|                                             char16_t *utf16_output) {
  291|  1.50k|  size_t extra_len{0};
  292|       |  // We potentially need to go back in time and find a leading byte.
  293|       |  // In theory '3' would be sufficient, but sometimes the error can go back
  294|       |  // quite far.
  295|  1.50k|  size_t how_far_back = prior_bytes;
  296|       |  // size_t how_far_back = 3; // 3 bytes in the past + current position
  297|       |  // if(how_far_back >= prior_bytes) { how_far_back = prior_bytes; }
  298|  1.50k|  bool found_leading_bytes{false};
  299|       |  // important: it is i <= how_far_back and not 'i < how_far_back'.
  300|  1.59k|  for (size_t i = 0; i <= how_far_back; i++) {
  ------------------
  |  Branch (300:22): [True: 1.54k, False: 52]
  ------------------
  301|  1.54k|    unsigned char byte = buf[-static_cast<std::ptrdiff_t>(i)];
  302|  1.54k|    found_leading_bytes = ((byte & 0b11000000) != 0b10000000);
  303|  1.54k|    if (found_leading_bytes) {
  ------------------
  |  Branch (303:9): [True: 1.45k, False: 84]
  ------------------
  304|  1.45k|      if (i > 0 && byte < 128) {
  ------------------
  |  Branch (304:11): [True: 32, False: 1.42k]
  |  Branch (304:20): [True: 32, False: 0]
  ------------------
  305|       |        // If we had to go back and the leading byte is ascii
  306|       |        // then we can stop right away.
  307|     32|        return result(error_code::TOO_LONG, 0 - i + 1);
  308|     32|      }
  309|  1.42k|      buf -= i;
  310|  1.42k|      extra_len = i;
  311|  1.42k|      break;
  312|  1.45k|    }
  313|  1.54k|  }
  314|       |  //
  315|       |  // It is possible for this function to return a negative count in its result.
  316|       |  // C++ Standard Section 18.1 defines size_t is in <cstddef> which is described
  317|       |  // in C Standard as <stddef.h>. C Standard Section 4.1.5 defines size_t as an
  318|       |  // unsigned integral type of the result of the sizeof operator
  319|       |  //
  320|       |  // An unsigned type will simply wrap round arithmetically (well defined).
  321|       |  //
  322|  1.47k|  if (!found_leading_bytes) {
  ------------------
  |  Branch (322:7): [True: 52, False: 1.42k]
  ------------------
  323|       |    // If how_far_back == 3, we may have four consecutive continuation bytes!!!
  324|       |    // [....] [continuation] [continuation] [continuation] | [buf is
  325|       |    // continuation] Or we possibly have a stream that does not start with a
  326|       |    // leading byte.
  327|     52|    return result(error_code::TOO_LONG, 0 - how_far_back);
  328|     52|  }
  329|  1.42k|  result res = convert_with_errors<endian>(buf, len + extra_len, utf16_output);
  330|  1.42k|  if (res.error) {
  ------------------
  |  Branch (330:7): [True: 756, False: 668]
  ------------------
  331|    756|    res.count -= extra_len;
  332|    756|  }
  333|  1.42k|  return res;
  334|  1.47k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf1613convert_validILNS_10endiannessE0EPKcQsr7simdutf6detailE22indexes_into_byte_likeIT0_EEEmS7_mPDs:
   14|  1.31k|                                         char16_t *utf16_output) {
   15|  1.31k|  size_t pos = 0;
   16|  1.31k|  char16_t *start{utf16_output};
   17|  4.16M|  while (pos < len) {
  ------------------
  |  Branch (17:10): [True: 4.16M, False: 1.31k]
  ------------------
   18|       |#if SIMDUTF_CPLUSPLUS23
   19|       |    if !consteval
   20|       |#endif
   21|  4.16M|    {                       // try to convert the next block of 8 ASCII bytes
   22|  4.16M|      if (pos + 8 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (22:11): [True: 4.15M, False: 4.18k]
  ------------------
   23|       |                            // they are ascii
   24|  4.15M|        uint64_t v;
   25|  4.15M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   26|  4.15M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (26:13): [True: 2.76M, False: 1.38M]
  ------------------
   27|  2.76M|          size_t final_pos = pos + 8;
   28|  24.9M|          while (pos < final_pos) {
  ------------------
  |  Branch (28:18): [True: 22.1M, False: 2.76M]
  ------------------
   29|  22.1M|            const char16_t byte = uint8_t(data[pos]);
   30|  22.1M|            *utf16_output++ =
   31|  22.1M|                !match_system(big_endian) ? u16_swap_bytes(byte) : byte;
  ------------------
  |  Branch (31:17): [Folded, False: 22.1M]
  ------------------
   32|  22.1M|            pos++;
   33|  22.1M|          }
   34|  2.76M|          continue;
   35|  2.76M|        }
   36|  4.15M|      }
   37|  4.16M|    }
   38|       |
   39|  1.39M|    auto leading_byte = uint8_t(data[pos]); // leading byte
   40|  1.39M|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (40:9): [True: 940k, False: 453k]
  ------------------
   41|       |      // converting one ASCII byte !!!
   42|   940k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (42:25): [Folded, False: 940k]
  ------------------
   43|   940k|                            ? char16_t(u16_swap_bytes(leading_byte))
   44|   940k|                            : char16_t(leading_byte);
   45|   940k|      pos++;
   46|   940k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (46:16): [True: 264k, False: 189k]
  ------------------
   47|       |      // We have a two-byte UTF-8, it should become
   48|       |      // a single UTF-16 word.
   49|   264k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (49:11): [True: 0, False: 264k]
  ------------------
   50|      0|        break;
   51|      0|      } // minimal bound checking
   52|   264k|      uint16_t code_point = uint16_t(((leading_byte & 0b00011111) << 6) |
   53|   264k|                                     (uint8_t(data[pos + 1]) & 0b00111111));
   54|       |      if constexpr (!match_system(big_endian)) {
   55|       |        code_point = u16_swap_bytes(uint16_t(code_point));
   56|       |      }
   57|   264k|      *utf16_output++ = char16_t(code_point);
   58|   264k|      pos += 2;
   59|   264k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (59:16): [True: 168k, False: 20.9k]
  ------------------
   60|       |      // We have a three-byte UTF-8, it should become
   61|       |      // a single UTF-16 word.
   62|   168k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (62:11): [True: 0, False: 168k]
  ------------------
   63|      0|        break;
   64|      0|      } // minimal bound checking
   65|   168k|      uint16_t code_point =
   66|   168k|          uint16_t(((leading_byte & 0b00001111) << 12) |
   67|   168k|                   ((uint8_t(data[pos + 1]) & 0b00111111) << 6) |
   68|   168k|                   (uint8_t(data[pos + 2]) & 0b00111111));
   69|       |      if constexpr (!match_system(big_endian)) {
   70|       |        code_point = u16_swap_bytes(uint16_t(code_point));
   71|       |      }
   72|   168k|      *utf16_output++ = char16_t(code_point);
   73|   168k|      pos += 3;
   74|   168k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (74:16): [True: 20.9k, False: 0]
  ------------------
   75|       |      // we have a 4-byte UTF-8 word.
   76|  20.9k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (76:11): [True: 0, False: 20.9k]
  ------------------
   77|      0|        break;
   78|      0|      } // minimal bound checking
   79|  20.9k|      uint32_t code_point = ((leading_byte & 0b00000111) << 18) |
   80|  20.9k|                            ((uint8_t(data[pos + 1]) & 0b00111111) << 12) |
   81|  20.9k|                            ((uint8_t(data[pos + 2]) & 0b00111111) << 6) |
   82|  20.9k|                            (uint8_t(data[pos + 3]) & 0b00111111);
   83|  20.9k|      code_point -= 0x10000;
   84|  20.9k|      uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10));
   85|  20.9k|      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
   86|       |      if constexpr (!match_system(big_endian)) {
   87|       |        high_surrogate = u16_swap_bytes(high_surrogate);
   88|       |        low_surrogate = u16_swap_bytes(low_surrogate);
   89|       |      }
   90|  20.9k|      *utf16_output++ = char16_t(high_surrogate);
   91|  20.9k|      *utf16_output++ = char16_t(low_surrogate);
   92|  20.9k|      pos += 4;
   93|  20.9k|    } else {
   94|       |      // we may have a continuation but we do not do error checking
   95|      0|      return 0;
   96|      0|    }
   97|  1.39M|  }
   98|  1.31k|  return utf16_output - start;
   99|  1.31k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf1613convert_validILNS_10endiannessE1EPKcQsr7simdutf6detailE22indexes_into_byte_likeIT0_EEEmS7_mPDs:
   14|  1.36k|                                         char16_t *utf16_output) {
   15|  1.36k|  size_t pos = 0;
   16|  1.36k|  char16_t *start{utf16_output};
   17|  4.41M|  while (pos < len) {
  ------------------
  |  Branch (17:10): [True: 4.41M, False: 1.36k]
  ------------------
   18|       |#if SIMDUTF_CPLUSPLUS23
   19|       |    if !consteval
   20|       |#endif
   21|  4.41M|    {                       // try to convert the next block of 8 ASCII bytes
   22|  4.41M|      if (pos + 8 <= len) { // if it is safe to read 8 more bytes, check that
  ------------------
  |  Branch (22:11): [True: 4.41M, False: 4.03k]
  ------------------
   23|       |                            // they are ascii
   24|  4.41M|        uint64_t v;
   25|  4.41M|        ::memcpy(&v, data + pos, sizeof(uint64_t));
   26|  4.41M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (26:13): [True: 3.71M, False: 695k]
  ------------------
   27|  3.71M|          size_t final_pos = pos + 8;
   28|  33.4M|          while (pos < final_pos) {
  ------------------
  |  Branch (28:18): [True: 29.7M, False: 3.71M]
  ------------------
   29|  29.7M|            const char16_t byte = uint8_t(data[pos]);
   30|  29.7M|            *utf16_output++ =
   31|  29.7M|                !match_system(big_endian) ? u16_swap_bytes(byte) : byte;
  ------------------
  |  Branch (31:17): [True: 29.7M, Folded]
  ------------------
   32|  29.7M|            pos++;
   33|  29.7M|          }
   34|  3.71M|          continue;
   35|  3.71M|        }
   36|  4.41M|      }
   37|  4.41M|    }
   38|       |
   39|   699k|    auto leading_byte = uint8_t(data[pos]); // leading byte
   40|   699k|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (40:9): [True: 350k, False: 348k]
  ------------------
   41|       |      // converting one ASCII byte !!!
   42|   350k|      *utf16_output++ = !match_system(big_endian)
  ------------------
  |  Branch (42:25): [True: 350k, Folded]
  ------------------
   43|   350k|                            ? char16_t(u16_swap_bytes(leading_byte))
   44|   350k|                            : char16_t(leading_byte);
   45|   350k|      pos++;
   46|   350k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (46:16): [True: 290k, False: 57.9k]
  ------------------
   47|       |      // We have a two-byte UTF-8, it should become
   48|       |      // a single UTF-16 word.
   49|   290k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (49:11): [True: 0, False: 290k]
  ------------------
   50|      0|        break;
   51|      0|      } // minimal bound checking
   52|   290k|      uint16_t code_point = uint16_t(((leading_byte & 0b00011111) << 6) |
   53|   290k|                                     (uint8_t(data[pos + 1]) & 0b00111111));
   54|   290k|      if constexpr (!match_system(big_endian)) {
   55|   290k|        code_point = u16_swap_bytes(uint16_t(code_point));
   56|   290k|      }
   57|   290k|      *utf16_output++ = char16_t(code_point);
   58|   290k|      pos += 2;
   59|   290k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (59:16): [True: 27.0k, False: 30.9k]
  ------------------
   60|       |      // We have a three-byte UTF-8, it should become
   61|       |      // a single UTF-16 word.
   62|  27.0k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (62:11): [True: 0, False: 27.0k]
  ------------------
   63|      0|        break;
   64|      0|      } // minimal bound checking
   65|  27.0k|      uint16_t code_point =
   66|  27.0k|          uint16_t(((leading_byte & 0b00001111) << 12) |
   67|  27.0k|                   ((uint8_t(data[pos + 1]) & 0b00111111) << 6) |
   68|  27.0k|                   (uint8_t(data[pos + 2]) & 0b00111111));
   69|  27.0k|      if constexpr (!match_system(big_endian)) {
   70|  27.0k|        code_point = u16_swap_bytes(uint16_t(code_point));
   71|  27.0k|      }
   72|  27.0k|      *utf16_output++ = char16_t(code_point);
   73|  27.0k|      pos += 3;
   74|  30.9k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (74:16): [True: 30.9k, False: 0]
  ------------------
   75|       |      // we have a 4-byte UTF-8 word.
   76|  30.9k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (76:11): [True: 0, False: 30.9k]
  ------------------
   77|      0|        break;
   78|      0|      } // minimal bound checking
   79|  30.9k|      uint32_t code_point = ((leading_byte & 0b00000111) << 18) |
   80|  30.9k|                            ((uint8_t(data[pos + 1]) & 0b00111111) << 12) |
   81|  30.9k|                            ((uint8_t(data[pos + 2]) & 0b00111111) << 6) |
   82|  30.9k|                            (uint8_t(data[pos + 3]) & 0b00111111);
   83|  30.9k|      code_point -= 0x10000;
   84|  30.9k|      uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10));
   85|  30.9k|      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
   86|  30.9k|      if constexpr (!match_system(big_endian)) {
   87|  30.9k|        high_surrogate = u16_swap_bytes(high_surrogate);
   88|  30.9k|        low_surrogate = u16_swap_bytes(low_surrogate);
   89|  30.9k|      }
   90|  30.9k|      *utf16_output++ = char16_t(high_surrogate);
   91|  30.9k|      *utf16_output++ = char16_t(low_surrogate);
   92|  30.9k|      pos += 4;
   93|  30.9k|    } else {
   94|       |      // we may have a continuation but we do not do error checking
   95|      0|      return 0;
   96|      0|    }
   97|   699k|  }
   98|  1.36k|  return utf16_output - start;
   99|  1.36k|}

simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf3230rewind_and_convert_with_errorsEmPKcmPDi:
  247|  1.52k|                                             char32_t *utf32_output) {
  248|  1.52k|  size_t extra_len{0};
  249|       |  // We potentially need to go back in time and find a leading byte.
  250|  1.52k|  size_t how_far_back = 3; // 3 bytes in the past + current position
  251|  1.52k|  if (how_far_back > prior_bytes) {
  ------------------
  |  Branch (251:7): [True: 776, False: 744]
  ------------------
  252|    776|    how_far_back = prior_bytes;
  253|    776|  }
  254|  1.52k|  bool found_leading_bytes{false};
  255|       |  // important: it is i <= how_far_back and not 'i < how_far_back'.
  256|  1.58k|  for (size_t i = 0; i <= how_far_back; i++) {
  ------------------
  |  Branch (256:22): [True: 1.52k, False: 60]
  ------------------
  257|  1.52k|    unsigned char byte = buf[-static_cast<std::ptrdiff_t>(i)];
  258|  1.52k|    found_leading_bytes = ((byte & 0b11000000) != 0b10000000);
  259|  1.52k|    if (found_leading_bytes) {
  ------------------
  |  Branch (259:9): [True: 1.46k, False: 68]
  ------------------
  260|  1.46k|      if (i > 0 && byte < 128) {
  ------------------
  |  Branch (260:11): [True: 8, False: 1.45k]
  |  Branch (260:20): [True: 8, False: 0]
  ------------------
  261|       |        // If we had to go back and the leading byte is ascii
  262|       |        // then we can stop right away.
  263|      8|        return result(error_code::TOO_LONG, 0 - i + 1);
  264|      8|      }
  265|  1.45k|      buf -= i;
  266|  1.45k|      extra_len = i;
  267|  1.45k|      break;
  268|  1.46k|    }
  269|  1.52k|  }
  270|       |  //
  271|       |  // It is possible for this function to return a negative count in its result.
  272|       |  // C++ Standard Section 18.1 defines size_t is in <cstddef> which is described
  273|       |  // in C Standard as <stddef.h>. C Standard Section 4.1.5 defines size_t as an
  274|       |  // unsigned integral type of the result of the sizeof operator
  275|       |  //
  276|       |  // An unsigned type will simply wrap round arithmetically (well defined).
  277|       |  //
  278|  1.51k|  if (!found_leading_bytes) {
  ------------------
  |  Branch (278:7): [True: 60, False: 1.45k]
  ------------------
  279|       |    // If how_far_back == 3, we may have four consecutive continuation bytes!!!
  280|       |    // [....] [continuation] [continuation] [continuation] | [buf is
  281|       |    // continuation] Or we possibly have a stream that does not start with a
  282|       |    // leading byte.
  283|     60|    return result(error_code::TOO_LONG, 0 - how_far_back);
  284|     60|  }
  285|       |
  286|  1.45k|  result res = convert_with_errors(buf, len + extra_len, utf32_output);
  287|  1.45k|  if (res.error) {
  ------------------
  |  Branch (287:7): [True: 756, False: 696]
  ------------------
  288|    756|    res.count -= extra_len;
  289|    756|  }
  290|  1.45k|  return res;
  291|  1.51k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf3219convert_with_errorsIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEENS_6resultES6_mPDi:
  119|  2.21k|                                               char32_t *utf32_output) {
  120|  2.21k|  size_t pos = 0;
  121|  2.21k|  char32_t *start{utf32_output};
  122|  3.35M|  while (pos < len) {
  ------------------
  |  Branch (122:10): [True: 3.35M, False: 1.04k]
  ------------------
  123|       |#if SIMDUTF_CPLUSPLUS23
  124|       |    if !consteval
  125|       |#endif
  126|  3.35M|    {
  127|       |      // try to convert the next block of 16 ASCII bytes
  128|  3.35M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (128:11): [True: 3.34M, False: 8.93k]
  ------------------
  129|       |                             // they are ascii
  130|  3.34M|        uint64_t v1;
  131|  3.34M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
  132|  3.34M|        uint64_t v2;
  133|  3.34M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
  134|  3.34M|        uint64_t v{v1 | v2};
  135|  3.34M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (135:13): [True: 1.81M, False: 1.53M]
  ------------------
  136|  1.81M|          size_t final_pos = pos + 16;
  137|  30.8M|          while (pos < final_pos) {
  ------------------
  |  Branch (137:18): [True: 28.9M, False: 1.81M]
  ------------------
  138|  28.9M|            *utf32_output++ = uint8_t(data[pos]);
  139|  28.9M|            pos++;
  140|  28.9M|          }
  141|  1.81M|          continue;
  142|  1.81M|        }
  143|  3.34M|      }
  144|  3.35M|    }
  145|  1.54M|    auto leading_byte = uint8_t(data[pos]); // leading byte
  146|  1.54M|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (146:9): [True: 1.20M, False: 344k]
  ------------------
  147|       |      // converting one ASCII byte !!!
  148|  1.20M|      *utf32_output++ = char32_t(leading_byte);
  149|  1.20M|      pos++;
  150|  1.20M|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (150:16): [True: 240k, False: 104k]
  ------------------
  151|       |      // We have a two-byte UTF-8
  152|   240k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (152:11): [True: 66, False: 240k]
  ------------------
  153|     66|        return result(error_code::TOO_SHORT, pos);
  154|     66|      } // minimal bound checking
  155|   240k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (155:11): [True: 162, False: 239k]
  ------------------
  156|    162|        return result(error_code::TOO_SHORT, pos);
  157|    162|      }
  158|       |      // range check
  159|   239k|      uint32_t code_point = (leading_byte & 0b00011111) << 6 |
  160|   239k|                            (uint8_t(data[pos + 1]) & 0b00111111);
  161|   239k|      if (code_point < 0x80) {
  ------------------
  |  Branch (161:11): [True: 30, False: 239k]
  ------------------
  162|     30|        return result(error_code::OVERLONG, pos);
  163|     30|      }
  164|   239k|      *utf32_output++ = char32_t(code_point);
  165|   239k|      pos += 2;
  166|   239k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (166:16): [True: 80.0k, False: 24.5k]
  ------------------
  167|       |      // We have a three-byte UTF-8
  168|  80.0k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (168:11): [True: 48, False: 80.0k]
  ------------------
  169|     48|        return result(error_code::TOO_SHORT, pos);
  170|     48|      } // minimal bound checking
  171|       |
  172|  80.0k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (172:11): [True: 156, False: 79.8k]
  ------------------
  173|    156|        return result(error_code::TOO_SHORT, pos);
  174|    156|      }
  175|  79.8k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (175:11): [True: 54, False: 79.8k]
  ------------------
  176|     54|        return result(error_code::TOO_SHORT, pos);
  177|     54|      }
  178|       |      // range check
  179|  79.8k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
  180|  79.8k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 6 |
  181|  79.8k|                            (uint8_t(data[pos + 2]) & 0b00111111);
  182|  79.8k|      if (code_point < 0x800) {
  ------------------
  |  Branch (182:11): [True: 30, False: 79.8k]
  ------------------
  183|     30|        return result(error_code::OVERLONG, pos);
  184|     30|      }
  185|  79.8k|      if (0xd7ff < code_point && code_point < 0xe000) {
  ------------------
  |  Branch (185:11): [True: 3.16k, False: 76.6k]
  |  Branch (185:34): [True: 6, False: 3.16k]
  ------------------
  186|      6|        return result(error_code::SURROGATE, pos);
  187|      6|      }
  188|  79.8k|      *utf32_output++ = char32_t(code_point);
  189|  79.8k|      pos += 3;
  190|  79.8k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (190:16): [True: 24.1k, False: 406]
  ------------------
  191|       |      // we have a 4-byte UTF-8 word.
  192|  24.1k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (192:11): [True: 30, False: 24.0k]
  ------------------
  193|     30|        return result(error_code::TOO_SHORT, pos);
  194|     30|      } // minimal bound checking
  195|  24.0k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (195:11): [True: 78, False: 24.0k]
  ------------------
  196|     78|        return result(error_code::TOO_SHORT, pos);
  197|     78|      }
  198|  24.0k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (198:11): [True: 30, False: 23.9k]
  ------------------
  199|     30|        return result(error_code::TOO_SHORT, pos);
  200|     30|      }
  201|  23.9k|      if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (201:11): [True: 18, False: 23.9k]
  ------------------
  202|     18|        return result(error_code::TOO_SHORT, pos);
  203|     18|      }
  204|       |
  205|       |      // range check
  206|  23.9k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
  207|  23.9k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 12 |
  208|  23.9k|                            (uint8_t(data[pos + 2]) & 0b00111111) << 6 |
  209|  23.9k|                            (uint8_t(data[pos + 3]) & 0b00111111);
  210|  23.9k|      if (code_point <= 0xffff) {
  ------------------
  |  Branch (210:11): [True: 30, False: 23.9k]
  ------------------
  211|     30|        return result(error_code::OVERLONG, pos);
  212|     30|      }
  213|  23.9k|      if (0x10ffff < code_point) {
  ------------------
  |  Branch (213:11): [True: 24, False: 23.9k]
  ------------------
  214|     24|        return result(error_code::TOO_LARGE, pos);
  215|     24|      }
  216|  23.9k|      *utf32_output++ = char32_t(code_point);
  217|  23.9k|      pos += 4;
  218|  23.9k|    } else {
  219|       |      // we either have too many continuation bytes or an invalid leading byte
  220|    406|      if ((leading_byte & 0b11000000) == 0b10000000) {
  ------------------
  |  Branch (220:11): [True: 220, False: 186]
  ------------------
  221|    220|        return result(error_code::TOO_LONG, pos);
  222|    220|      } else {
  223|    186|        return result(error_code::HEADER_BITS, pos);
  224|    186|      }
  225|    406|    }
  226|  1.54M|  }
  227|  1.04k|  return result(error_code::SUCCESS, utf32_output - start);
  228|  2.21k|}
simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf327convertIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEEmS6_mPDi:
   14|  1.67k|                                   char32_t *utf32_output) {
   15|  1.67k|  size_t pos = 0;
   16|  1.67k|  char32_t *start{utf32_output};
   17|  2.18M|  while (pos < len) {
  ------------------
  |  Branch (17:10): [True: 2.18M, False: 838]
  ------------------
   18|       |#if SIMDUTF_CPLUSPLUS23
   19|       |    if !consteval
   20|       |#endif
   21|  2.18M|    {
   22|       |      // try to convert the next block of 16 ASCII bytes
   23|  2.18M|      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
  ------------------
  |  Branch (23:11): [True: 2.18M, False: 6.04k]
  ------------------
   24|       |                             // they are ascii
   25|  2.18M|        uint64_t v1;
   26|  2.18M|        ::memcpy(&v1, data + pos, sizeof(uint64_t));
   27|  2.18M|        uint64_t v2;
   28|  2.18M|        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
   29|  2.18M|        uint64_t v{v1 | v2};
   30|  2.18M|        if ((v & 0x8080808080808080) == 0) {
  ------------------
  |  Branch (30:13): [True: 1.28M, False: 896k]
  ------------------
   31|  1.28M|          size_t final_pos = pos + 16;
   32|  21.8M|          while (pos < final_pos) {
  ------------------
  |  Branch (32:18): [True: 20.5M, False: 1.28M]
  ------------------
   33|  20.5M|            *utf32_output++ = uint8_t(data[pos]);
   34|  20.5M|            pos++;
   35|  20.5M|          }
   36|  1.28M|          continue;
   37|  1.28M|        }
   38|  2.18M|      }
   39|  2.18M|    }
   40|   902k|    auto leading_byte = uint8_t(data[pos]); // leading byte
   41|   902k|    if (leading_byte < 0b10000000) {
  ------------------
  |  Branch (41:9): [True: 721k, False: 181k]
  ------------------
   42|       |      // converting one ASCII byte !!!
   43|   721k|      *utf32_output++ = char32_t(leading_byte);
   44|   721k|      pos++;
   45|   721k|    } else if ((leading_byte & 0b11100000) == 0b11000000) {
  ------------------
  |  Branch (45:16): [True: 116k, False: 64.7k]
  ------------------
   46|       |      // We have a two-byte UTF-8
   47|   116k|      if (pos + 1 >= len) {
  ------------------
  |  Branch (47:11): [True: 48, False: 116k]
  ------------------
   48|     48|        return 0;
   49|     48|      } // minimal bound checking
   50|   116k|      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (50:11): [True: 96, False: 116k]
  ------------------
   51|     96|        return 0;
   52|     96|      }
   53|       |      // range check
   54|   116k|      uint32_t code_point = (leading_byte & 0b00011111) << 6 |
   55|   116k|                            (uint8_t(data[pos + 1]) & 0b00111111);
   56|   116k|      if (code_point < 0x80) {
  ------------------
  |  Branch (56:11): [True: 34, False: 116k]
  ------------------
   57|     34|        return 0;
   58|     34|      }
   59|   116k|      *utf32_output++ = char32_t(code_point);
   60|   116k|      pos += 2;
   61|   116k|    } else if ((leading_byte & 0b11110000) == 0b11100000) {
  ------------------
  |  Branch (61:16): [True: 54.9k, False: 9.75k]
  ------------------
   62|       |      // We have a three-byte UTF-8
   63|  54.9k|      if (pos + 2 >= len) {
  ------------------
  |  Branch (63:11): [True: 48, False: 54.9k]
  ------------------
   64|     48|        return 0;
   65|     48|      } // minimal bound checking
   66|       |
   67|  54.9k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (67:11): [True: 50, False: 54.8k]
  ------------------
   68|     50|        return 0;
   69|     50|      }
   70|  54.8k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (70:11): [True: 34, False: 54.8k]
  ------------------
   71|     34|        return 0;
   72|     34|      }
   73|       |      // range check
   74|  54.8k|      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
   75|  54.8k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 6 |
   76|  54.8k|                            (uint8_t(data[pos + 2]) & 0b00111111);
   77|  54.8k|      if (code_point < 0x800 || (0xd7ff < code_point && code_point < 0xe000)) {
  ------------------
  |  Branch (77:11): [True: 28, False: 54.7k]
  |  Branch (77:34): [True: 726, False: 54.0k]
  |  Branch (77:57): [True: 14, False: 712]
  ------------------
   78|     42|        return 0;
   79|     42|      }
   80|  54.7k|      *utf32_output++ = char32_t(code_point);
   81|  54.7k|      pos += 3;
   82|  54.7k|    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
  ------------------
  |  Branch (82:16): [True: 9.46k, False: 294]
  ------------------
   83|       |      // we have a 4-byte UTF-8 word.
   84|  9.46k|      if (pos + 3 >= len) {
  ------------------
  |  Branch (84:11): [True: 18, False: 9.44k]
  ------------------
   85|     18|        return 0;
   86|     18|      } // minimal bound checking
   87|  9.44k|      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (87:11): [True: 52, False: 9.39k]
  ------------------
   88|     52|        return 0;
   89|     52|      }
   90|  9.39k|      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (90:11): [True: 26, False: 9.36k]
  ------------------
   91|     26|        return 0;
   92|     26|      }
   93|  9.36k|      if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) {
  ------------------
  |  Branch (93:11): [True: 20, False: 9.34k]
  ------------------
   94|     20|        return 0;
   95|     20|      }
   96|       |
   97|       |      // range check
   98|  9.34k|      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
   99|  9.34k|                            (uint8_t(data[pos + 1]) & 0b00111111) << 12 |
  100|  9.34k|                            (uint8_t(data[pos + 2]) & 0b00111111) << 6 |
  101|  9.34k|                            (uint8_t(data[pos + 3]) & 0b00111111);
  102|  9.34k|      if (code_point <= 0xffff || 0x10ffff < code_point) {
  ------------------
  |  Branch (102:11): [True: 32, False: 9.31k]
  |  Branch (102:35): [True: 42, False: 9.27k]
  ------------------
  103|     74|        return 0;
  104|     74|      }
  105|  9.27k|      *utf32_output++ = char32_t(code_point);
  106|  9.27k|      pos += 4;
  107|  9.27k|    } else {
  108|    294|      return 0;
  109|    294|    }
  110|   902k|  }
  111|    838|  return utf32_output - start;
  112|  1.67k|}

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

_ZNK7simdutf8fallback14implementation13validate_utf8EPKcm:
   37|  3.42k|implementation::validate_utf8(const char *buf, size_t len) const noexcept {
   38|  3.42k|  return scalar::utf8::validate(buf, len);
   39|  3.42k|}
_ZNK7simdutf8fallback14implementation25validate_utf8_with_errorsEPKcm:
   44|  3.42k|    const char *buf, size_t len) const noexcept {
   45|  3.42k|  return scalar::utf8::validate_with_errors(buf, len);
   46|  3.42k|}
_ZNK7simdutf8fallback14implementation16validate_utf16leEPKDsm:
   77|  1.39k|                                 size_t len) const noexcept {
   78|  1.39k|  return scalar::utf16::validate<endianness::LITTLE>(buf, len);
   79|  1.39k|}
_ZNK7simdutf8fallback14implementation16validate_utf16beEPKDsm:
   85|  1.30k|                                 size_t len) const noexcept {
   86|  1.30k|  return scalar::utf16::validate<endianness::BIG>(buf, len);
   87|  1.30k|}
_ZNK7simdutf8fallback14implementation28validate_utf16le_with_errorsEPKDsm:
   90|  1.39k|    const char16_t *buf, size_t len) const noexcept {
   91|  1.39k|  return scalar::utf16::validate_with_errors<endianness::LITTLE>(buf, len);
   92|  1.39k|}
_ZNK7simdutf8fallback14implementation28validate_utf16be_with_errorsEPKDsm:
   95|  1.30k|    const char16_t *buf, size_t len) const noexcept {
   96|  1.30k|  return scalar::utf16::validate_with_errors<endianness::BIG>(buf, len);
   97|  1.30k|}
_ZNK7simdutf8fallback14implementation14validate_utf32EPKDim:
  114|  2.49k|implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept {
  115|  2.49k|  return scalar::utf32::validate(buf, len);
  116|  2.49k|}
_ZNK7simdutf8fallback14implementation26validate_utf32_with_errorsEPKDim:
  121|  2.49k|    const char32_t *buf, size_t len) const noexcept {
  122|  2.49k|  return scalar::utf32::validate_with_errors(buf, len);
  123|  2.49k|}
_ZNK7simdutf8fallback14implementation22convert_latin1_to_utf8EPKcmPc:
  128|    614|    const char *buf, size_t len, char *utf8_output) const noexcept {
  129|    614|  return scalar::latin1_to_utf8::convert(buf, len, utf8_output);
  130|    614|}
_ZNK7simdutf8fallback14implementation25convert_latin1_to_utf16leEPKcmPDs:
  135|     88|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  136|     88|  return scalar::latin1_to_utf16::convert<endianness::LITTLE>(buf, len,
  137|     88|                                                              utf16_output);
  138|     88|}
_ZNK7simdutf8fallback14implementation25convert_latin1_to_utf16beEPKcmPDs:
  141|    100|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  142|    100|  return scalar::latin1_to_utf16::convert<endianness::BIG>(buf, len,
  143|    100|                                                           utf16_output);
  144|    100|}
_ZNK7simdutf8fallback14implementation23convert_latin1_to_utf32EPKcmPDi:
  149|    126|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  150|    126|  return scalar::latin1_to_utf32::convert(buf, len, utf32_output);
  151|    126|}
_ZNK7simdutf8fallback14implementation22convert_utf8_to_latin1EPKcmPc:
  156|    576|    const char *buf, size_t len, char *latin1_output) const noexcept {
  157|    576|  return scalar::utf8_to_latin1::convert(buf, len, latin1_output);
  158|    576|}
_ZNK7simdutf8fallback14implementation34convert_utf8_to_latin1_with_errorsEPKcmPc:
  161|    500|    const char *buf, size_t len, char *latin1_output) const noexcept {
  162|    500|  return scalar::utf8_to_latin1::convert_with_errors(buf, len, latin1_output);
  163|    500|}
_ZNK7simdutf8fallback14implementation23convert_utf8_to_utf16leEPKcmPDs:
  173|    754|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  174|    754|  return scalar::utf8_to_utf16::convert<endianness::LITTLE>(buf, len,
  175|    754|                                                            utf16_output);
  176|    754|}
_ZNK7simdutf8fallback14implementation23convert_utf8_to_utf16beEPKcmPDs:
  179|    758|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  180|    758|  return scalar::utf8_to_utf16::convert<endianness::BIG>(buf, len,
  181|    758|                                                         utf16_output);
  182|    758|}
_ZNK7simdutf8fallback14implementation35convert_utf8_to_utf16le_with_errorsEPKcmPDs:
  185|    568|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  186|    568|  return scalar::utf8_to_utf16::convert_with_errors<endianness::LITTLE>(
  187|    568|      buf, len, utf16_output);
  188|    568|}
_ZNK7simdutf8fallback14implementation35convert_utf8_to_utf16be_with_errorsEPKcmPDs:
  191|    758|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  192|    758|  return scalar::utf8_to_utf16::convert_with_errors<endianness::BIG>(
  193|    758|      buf, len, utf16_output);
  194|    758|}
_ZNK7simdutf8fallback14implementation29convert_valid_utf8_to_utf16leEPKcmPDs:
  197|    438|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  198|    438|  return scalar::utf8_to_utf16::convert_valid<endianness::LITTLE>(buf, len,
  199|    438|                                                                  utf16_output);
  200|    438|}
_ZNK7simdutf8fallback14implementation29convert_valid_utf8_to_utf16beEPKcmPDs:
  203|    456|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  204|    456|  return scalar::utf8_to_utf16::convert_valid<endianness::BIG>(buf, len,
  205|    456|                                                               utf16_output);
  206|    456|}
_ZNK7simdutf8fallback14implementation21convert_utf8_to_utf32EPKcmPDi:
  211|    758|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  212|    758|  return scalar::utf8_to_utf32::convert(buf, len, utf32_output);
  213|    758|}
_ZNK7simdutf8fallback14implementation33convert_utf8_to_utf32_with_errorsEPKcmPDi:
  216|    764|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  217|    764|  return scalar::utf8_to_utf32::convert_with_errors(buf, len, utf32_output);
  218|    764|}
_ZNK7simdutf8fallback14implementation27convert_valid_utf8_to_utf32EPKcmPDi:
  221|    454|    const char *input, size_t size, char32_t *utf32_output) const noexcept {
  222|    454|  return scalar::utf8_to_utf32::convert_valid(input, size, utf32_output);
  223|    454|}
_ZNK7simdutf8fallback14implementation25convert_utf16le_to_latin1EPKDsmPc:
  228|    132|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  229|    132|  return scalar::utf16_to_latin1::convert<endianness::LITTLE>(buf, len,
  230|    132|                                                              latin1_output);
  231|    132|}
_ZNK7simdutf8fallback14implementation25convert_utf16be_to_latin1EPKDsmPc:
  234|    102|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  235|    102|  return scalar::utf16_to_latin1::convert<endianness::BIG>(buf, len,
  236|    102|                                                           latin1_output);
  237|    102|}
_ZNK7simdutf8fallback14implementation37convert_utf16le_to_latin1_with_errorsEPKDsmPc:
  241|    282|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  242|    282|  return scalar::utf16_to_latin1::convert_with_errors<endianness::LITTLE>(
  243|    282|      buf, len, latin1_output);
  244|    282|}
_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|    410|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  269|    410|  return scalar::utf16_to_utf8::convert<endianness::LITTLE>(buf, len,
  270|    410|                                                            utf8_output);
  271|    410|}
_ZNK7simdutf8fallback14implementation23convert_utf16be_to_utf8EPKDsmPc:
  274|    342|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  275|    342|  return scalar::utf16_to_utf8::convert<endianness::BIG>(buf, len, utf8_output);
  276|    342|}
_ZNK7simdutf8fallback14implementation35convert_utf16le_to_utf8_with_errorsEPKDsmPc:
  279|    678|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  280|    678|  return scalar::utf16_to_utf8::convert_with_errors<endianness::LITTLE>(
  281|    678|      buf, len, utf8_output);
  282|    678|}
_ZNK7simdutf8fallback14implementation35convert_utf16be_to_utf8_with_errorsEPKDsmPc:
  285|    716|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  286|    716|  return scalar::utf16_to_utf8::convert_with_errors<endianness::BIG>(
  287|    716|      buf, len, utf8_output);
  288|    716|}
_ZNK7simdutf8fallback14implementation29convert_valid_utf16le_to_utf8EPKDsmPc:
  291|    428|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  292|    428|  return scalar::utf16_to_utf8::convert_valid<endianness::LITTLE>(buf, len,
  293|    428|                                                                  utf8_output);
  294|    428|}
_ZNK7simdutf8fallback14implementation29convert_valid_utf16be_to_utf8EPKDsmPc:
  297|    400|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  298|    400|  return scalar::utf16_to_utf8::convert_valid<endianness::BIG>(buf, len,
  299|    400|                                                               utf8_output);
  300|    400|}
_ZNK7simdutf8fallback14implementation23convert_utf32_to_latin1EPKDimPc:
  305|    178|    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
  306|    178|  return scalar::utf32_to_latin1::convert(buf, len, latin1_output);
  307|    178|}
_ZNK7simdutf8fallback14implementation35convert_utf32_to_latin1_with_errorsEPKDimPc:
  310|    408|    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
  311|    408|  return scalar::utf32_to_latin1::convert_with_errors(buf, len, latin1_output);
  312|    408|}
_ZNK7simdutf8fallback14implementation21convert_utf32_to_utf8EPKDimPc:
  322|    636|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  323|    636|  return scalar::utf32_to_utf8::convert(buf, len, utf8_output);
  324|    636|}
_ZNK7simdutf8fallback14implementation33convert_utf32_to_utf8_with_errorsEPKDimPc:
  327|    892|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  328|    892|  return scalar::utf32_to_utf8::convert_with_errors(buf, len, utf8_output);
  329|    892|}
_ZNK7simdutf8fallback14implementation27convert_valid_utf32_to_utf8EPKDimPc:
  332|    224|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  333|    224|  return scalar::utf32_to_utf8::convert_valid(buf, len, utf8_output);
  334|    224|}
_ZNK7simdutf8fallback14implementation24convert_utf32_to_utf16leEPKDimPDs:
  339|    496|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  340|    496|  return scalar::utf32_to_utf16::convert<endianness::LITTLE>(buf, len,
  341|    496|                                                             utf16_output);
  342|    496|}
_ZNK7simdutf8fallback14implementation24convert_utf32_to_utf16beEPKDimPDs:
  345|    508|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  346|    508|  return scalar::utf32_to_utf16::convert<endianness::BIG>(buf, len,
  347|    508|                                                          utf16_output);
  348|    508|}
_ZNK7simdutf8fallback14implementation36convert_utf32_to_utf16le_with_errorsEPKDimPDs:
  351|    658|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  352|    658|  return scalar::utf32_to_utf16::convert_with_errors<endianness::LITTLE>(
  353|    658|      buf, len, utf16_output);
  354|    658|}
_ZNK7simdutf8fallback14implementation36convert_utf32_to_utf16be_with_errorsEPKDimPDs:
  357|    592|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  358|    592|  return scalar::utf32_to_utf16::convert_with_errors<endianness::BIG>(
  359|    592|      buf, len, utf16_output);
  360|    592|}
_ZNK7simdutf8fallback14implementation30convert_valid_utf32_to_utf16leEPKDimPDs:
  363|    242|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  364|    242|  return scalar::utf32_to_utf16::convert_valid<endianness::LITTLE>(
  365|    242|      buf, len, utf16_output);
  366|    242|}
_ZNK7simdutf8fallback14implementation30convert_valid_utf32_to_utf16beEPKDimPDs:
  369|    126|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  370|    126|  return scalar::utf32_to_utf16::convert_valid<endianness::BIG>(buf, len,
  371|    126|                                                                utf16_output);
  372|    126|}
_ZNK7simdutf8fallback14implementation24convert_utf16le_to_utf32EPKDsmPDi:
  375|    294|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  376|    294|  return scalar::utf16_to_utf32::convert<endianness::LITTLE>(buf, len,
  377|    294|                                                             utf32_output);
  378|    294|}
_ZNK7simdutf8fallback14implementation24convert_utf16be_to_utf32EPKDsmPDi:
  381|    238|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  382|    238|  return scalar::utf16_to_utf32::convert<endianness::BIG>(buf, len,
  383|    238|                                                          utf32_output);
  384|    238|}
_ZNK7simdutf8fallback14implementation36convert_utf16le_to_utf32_with_errorsEPKDsmPDi:
  387|    408|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  388|    408|  return scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
  389|    408|      buf, len, utf32_output);
  390|    408|}
_ZNK7simdutf8fallback14implementation36convert_utf16be_to_utf32_with_errorsEPKDsmPDi:
  393|    362|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  394|    362|  return scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
  395|    362|      buf, len, utf32_output);
  396|    362|}
_ZNK7simdutf8fallback14implementation30convert_valid_utf16le_to_utf32EPKDsmPDi:
  399|    120|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  400|    120|  return scalar::utf16_to_utf32::convert_valid<endianness::LITTLE>(
  401|    120|      buf, len, utf32_output);
  402|    120|}
_ZNK7simdutf8fallback14implementation30convert_valid_utf16be_to_utf32EPKDsmPDi:
  405|    146|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  406|    146|  return scalar::utf16_to_utf32::convert_valid<endianness::BIG>(buf, len,
  407|    146|                                                                utf32_output);
  408|    146|}
_ZNK7simdutf8fallback14implementation13count_utf16leEPKDsm:
  419|  1.39k|    const char16_t *input, size_t length) const noexcept {
  420|  1.39k|  return scalar::utf16::count_code_points<endianness::LITTLE>(input, length);
  421|  1.39k|}
_ZNK7simdutf8fallback14implementation13count_utf16beEPKDsm:
  424|  1.30k|    const char16_t *input, size_t length) const noexcept {
  425|  1.30k|  return scalar::utf16::count_code_points<endianness::BIG>(input, length);
  426|  1.30k|}
_ZNK7simdutf8fallback14implementation10count_utf8EPKcm:
  431|  3.42k|implementation::count_utf8(const char *input, size_t length) const noexcept {
  432|  3.42k|  return scalar::utf8::count_code_points(input, length);
  433|  3.42k|}
_ZNK7simdutf8fallback14implementation23latin1_length_from_utf8EPKcm:
  438|    538|    const char *buf, size_t len) const noexcept {
  439|    538|  return scalar::utf8::count_code_points(buf, len);
  440|    538|}
_ZNK7simdutf8fallback14implementation23utf8_length_from_latin1EPKcm:
  445|    307|    const char *input, size_t length) const noexcept {
  446|    307|  return scalar::latin1_to_utf8::utf8_length_from_latin1(input, length);
  447|    307|}
_ZNK7simdutf8fallback14implementation24utf8_length_from_utf16leEPKDsm:
  452|    767|    const char16_t *input, size_t length) const noexcept {
  453|    767|  return scalar::utf16::utf8_length_from_utf16<endianness::LITTLE>(input,
  454|    767|                                                                   length);
  455|    767|}
_ZNK7simdutf8fallback14implementation24utf8_length_from_utf16beEPKDsm:
  458|    741|    const char16_t *input, size_t length) const noexcept {
  459|    741|  return scalar::utf16::utf8_length_from_utf16<endianness::BIG>(input, length);
  460|    741|}
_ZNK7simdutf8fallback14implementation25utf32_length_from_utf16leEPKDsm:
  465|    417|    const char16_t *input, size_t length) const noexcept {
  466|    417|  return scalar::utf16::utf32_length_from_utf16<endianness::LITTLE>(input,
  467|    417|                                                                    length);
  468|    417|}
_ZNK7simdutf8fallback14implementation25utf32_length_from_utf16beEPKDsm:
  471|    389|    const char16_t *input, size_t length) const noexcept {
  472|    389|  return scalar::utf16::utf32_length_from_utf16<endianness::BIG>(input, length);
  473|    389|}
_ZNK7simdutf8fallback14implementation22utf16_length_from_utf8EPKcm:
  478|  1.88k|    const char *input, size_t length) const noexcept {
  479|  1.88k|  return scalar::utf8::utf16_length_from_utf8(input, length);
  480|  1.88k|}
_ZNK7simdutf8fallback14implementation22utf8_length_from_utf32EPKDim:
  513|    884|    const char32_t *input, size_t length) const noexcept {
  514|    884|  return scalar::utf32::utf8_length_from_utf32(input, length);
  515|    884|}
_ZNK7simdutf8fallback14implementation23utf16_length_from_utf32EPKDim:
  520|  1.32k|    const char32_t *input, size_t length) const noexcept {
  521|  1.32k|  return scalar::utf32::utf16_length_from_utf32(input, length);
  522|  1.32k|}
_ZNK7simdutf8fallback14implementation22utf32_length_from_utf8EPKcm:
  527|    995|    const char *input, size_t length) const noexcept {
  528|    995|  return scalar::utf8::count_code_points(input, length);
  529|    995|}

simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_116buf_block_readerILm64EE13get_remainderEPh:
   57|  5.98k|buf_block_reader<STEP_SIZE>::get_remainder(uint8_t *dst) const {
   58|  5.98k|  if (len == idx) {
  ------------------
  |  Branch (58:7): [True: 36, False: 5.94k]
  ------------------
   59|     36|    return 0;
   60|     36|  } // memcpy(dst, null, 0) will trigger an error with some sanitizers
   61|  5.94k|  std::memset(dst, 0x20,
   62|  5.94k|              STEP_SIZE); // std::memset STEP_SIZE because it is more efficient
   63|       |                          // to write out 8 or 16 bytes at once.
   64|  5.94k|  std::memcpy(dst, buf + idx, len - idx);
   65|  5.94k|  return len - idx;
   66|  5.98k|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_116buf_block_readerILm64EE13get_remainderEPh:
   57|  5.98k|buf_block_reader<STEP_SIZE>::get_remainder(uint8_t *dst) const {
   58|  5.98k|  if (len == idx) {
  ------------------
  |  Branch (58:7): [True: 36, False: 5.94k]
  ------------------
   59|     36|    return 0;
   60|     36|  } // memcpy(dst, null, 0) will trigger an error with some sanitizers
   61|  5.94k|  std::memset(dst, 0x20,
   62|  5.94k|              STEP_SIZE); // std::memset STEP_SIZE because it is more efficient
   63|       |                          // to write out 8 or 16 bytes at once.
   64|  5.94k|  std::memcpy(dst, buf + idx, len - idx);
   65|  5.94k|  return len - idx;
   66|  5.98k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_116buf_block_readerILm64EEC2EPKhm:
   36|  6.84k|    : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE},
  ------------------
  |  Branch (36:42): [True: 2.54k, False: 4.29k]
  ------------------
   37|  6.84k|      idx{0} {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_116buf_block_readerILm64EE14has_full_blockEv:
   45|  4.98M|simdutf_really_inline bool buf_block_reader<STEP_SIZE>::has_full_block() const {
   46|  4.98M|  return idx < lenminusstep;
   47|  4.98M|}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_116buf_block_readerILm64EE10full_blockEv:
   51|  4.98M|buf_block_reader<STEP_SIZE>::full_block() const {
   52|  4.98M|  return &buf[idx];
   53|  4.98M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_116buf_block_readerILm64EE7advanceEv:
   69|  4.98M|simdutf_really_inline void buf_block_reader<STEP_SIZE>::advance() {
   70|  4.98M|  idx += STEP_SIZE;
   71|  4.98M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_116buf_block_readerILm64EEC2EPKhm:
   36|  6.84k|    : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE},
  ------------------
  |  Branch (36:42): [True: 2.54k, False: 4.29k]
  ------------------
   37|  6.84k|      idx{0} {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_116buf_block_readerILm64EE14has_full_blockEv:
   45|  4.98M|simdutf_really_inline bool buf_block_reader<STEP_SIZE>::has_full_block() const {
   46|  4.98M|  return idx < lenminusstep;
   47|  4.98M|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_116buf_block_readerILm64EE10full_blockEv:
   51|  4.98M|buf_block_reader<STEP_SIZE>::full_block() const {
   52|  4.98M|  return &buf[idx];
   53|  4.98M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_116buf_block_readerILm64EE7advanceEv:
   69|  4.98M|simdutf_really_inline void buf_block_reader<STEP_SIZE>::advance() {
   70|  4.98M|  idx += STEP_SIZE;
   71|  4.98M|}

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

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1631utf8_length_from_utf16_bytemaskILNS_10endiannessE0EEEmPKDsm:
   10|    767|                                                             size_t size) {
   11|    767|  size_t pos = 0;
   12|       |
   13|    767|  using vector_u16 = simd16<uint16_t>;
   14|    767|  constexpr size_t N = vector_u16::ELEMENTS;
   15|       |
   16|    767|  const auto one = vector_u16::splat(1);
   17|       |
   18|    767|  auto v_count = vector_u16::zero();
   19|       |
   20|       |  // each char16 yields at least one byte
   21|    767|  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|    767|  constexpr size_t max_iterations = 65535 / 2;
   26|    767|  size_t iteration = max_iterations;
   27|       |
   28|  1.09M|  for (; pos < size / N * N; pos += N) {
  ------------------
  |  Branch (28:10): [True: 1.09M, False: 767]
  ------------------
   29|  1.09M|    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.09M|    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.09M|    const auto c0 = min(input & uint16_t(0xff80), one);
   39|       |
   40|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
   41|  1.09M|    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.09M|    v_count += c0;
   68|  1.09M|    v_count += c1;
   69|  1.09M|    v_count += vector_u16(is_surrogate);
   70|       |
   71|  1.09M|    iteration -= 1;
   72|  1.09M|    if (iteration == 0) {
  ------------------
  |  Branch (72:9): [True: 17, False: 1.09M]
  ------------------
   73|     17|      count += v_count.sum();
   74|     17|      v_count = vector_u16::zero();
   75|     17|      iteration = max_iterations;
   76|     17|    }
   77|  1.09M|  }
   78|       |
   79|    767|  if (iteration > 0) {
  ------------------
  |  Branch (79:7): [True: 767, False: 0]
  ------------------
   80|    767|    count += v_count.sum();
   81|    767|  }
   82|       |
   83|    767|  return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos,
   84|    767|                                                                   size - pos);
   85|    767|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1631utf8_length_from_utf16_bytemaskILNS_10endiannessE1EEEmPKDsm:
   10|    741|                                                             size_t size) {
   11|    741|  size_t pos = 0;
   12|       |
   13|    741|  using vector_u16 = simd16<uint16_t>;
   14|    741|  constexpr size_t N = vector_u16::ELEMENTS;
   15|       |
   16|    741|  const auto one = vector_u16::splat(1);
   17|       |
   18|    741|  auto v_count = vector_u16::zero();
   19|       |
   20|       |  // each char16 yields at least one byte
   21|    741|  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|    741|  constexpr size_t max_iterations = 65535 / 2;
   26|    741|  size_t iteration = max_iterations;
   27|       |
   28|  1.10M|  for (; pos < size / N * N; pos += N) {
  ------------------
  |  Branch (28:10): [True: 1.09M, False: 741]
  ------------------
   29|  1.09M|    auto input = vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos));
   30|  1.09M|    if constexpr (!match_system(big_endian)) {
   31|  1.09M|      input = input.swap_bytes();
   32|  1.09M|    }
   33|       |    // 0xd800 .. 0xdbff - low surrogate
   34|       |    // 0xdc00 .. 0xdfff - high surrogate
   35|  1.09M|    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.09M|    const auto c0 = min(input & uint16_t(0xff80), one);
   39|       |
   40|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
   41|  1.09M|    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.09M|    v_count += c0;
   68|  1.09M|    v_count += c1;
   69|  1.09M|    v_count += vector_u16(is_surrogate);
   70|       |
   71|  1.09M|    iteration -= 1;
   72|  1.09M|    if (iteration == 0) {
  ------------------
  |  Branch (72:9): [True: 16, False: 1.09M]
  ------------------
   73|     16|      count += v_count.sum();
   74|     16|      v_count = vector_u16::zero();
   75|     16|      iteration = max_iterations;
   76|     16|    }
   77|  1.09M|  }
   78|       |
   79|    741|  if (iteration > 0) {
  ------------------
  |  Branch (79:7): [True: 741, False: 0]
  ------------------
   80|    741|    count += v_count.sum();
   81|    741|  }
   82|       |
   83|    741|  return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos,
   84|    741|                                                                   size - pos);
   85|    741|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1631utf8_length_from_utf16_bytemaskILNS_10endiannessE0EEEmPKDsm:
   10|    767|                                                             size_t size) {
   11|    767|  size_t pos = 0;
   12|       |
   13|    767|  using vector_u16 = simd16<uint16_t>;
   14|    767|  constexpr size_t N = vector_u16::ELEMENTS;
   15|       |
   16|    767|  const auto one = vector_u16::splat(1);
   17|       |
   18|    767|  auto v_count = vector_u16::zero();
   19|       |
   20|       |  // each char16 yields at least one byte
   21|    767|  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|    767|  constexpr size_t max_iterations = 65535 / 2;
   26|    767|  size_t iteration = max_iterations;
   27|       |
   28|  2.18M|  for (; pos < size / N * N; pos += N) {
  ------------------
  |  Branch (28:10): [True: 2.18M, False: 767]
  ------------------
   29|  2.18M|    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.18M|    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.18M|    const auto c0 = min(input & uint16_t(0xff80), one);
   39|       |
   40|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
   41|  2.18M|    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.18M|    v_count += c0;
   68|  2.18M|    v_count += c1;
   69|  2.18M|    v_count += vector_u16(is_surrogate);
   70|       |
   71|  2.18M|    iteration -= 1;
   72|  2.18M|    if (iteration == 0) {
  ------------------
  |  Branch (72:9): [True: 47, False: 2.18M]
  ------------------
   73|     47|      count += v_count.sum();
   74|     47|      v_count = vector_u16::zero();
   75|     47|      iteration = max_iterations;
   76|     47|    }
   77|  2.18M|  }
   78|       |
   79|    767|  if (iteration > 0) {
  ------------------
  |  Branch (79:7): [True: 767, False: 0]
  ------------------
   80|    767|    count += v_count.sum();
   81|    767|  }
   82|       |
   83|    767|  return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos,
   84|    767|                                                                   size - pos);
   85|    767|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1631utf8_length_from_utf16_bytemaskILNS_10endiannessE1EEEmPKDsm:
   10|    741|                                                             size_t size) {
   11|    741|  size_t pos = 0;
   12|       |
   13|    741|  using vector_u16 = simd16<uint16_t>;
   14|    741|  constexpr size_t N = vector_u16::ELEMENTS;
   15|       |
   16|    741|  const auto one = vector_u16::splat(1);
   17|       |
   18|    741|  auto v_count = vector_u16::zero();
   19|       |
   20|       |  // each char16 yields at least one byte
   21|    741|  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|    741|  constexpr size_t max_iterations = 65535 / 2;
   26|    741|  size_t iteration = max_iterations;
   27|       |
   28|  2.20M|  for (; pos < size / N * N; pos += N) {
  ------------------
  |  Branch (28:10): [True: 2.19M, False: 741]
  ------------------
   29|  2.19M|    auto input = vector_u16::load(reinterpret_cast<const uint16_t *>(in + pos));
   30|  2.19M|    if constexpr (!match_system(big_endian)) {
   31|  2.19M|      input = input.swap_bytes();
   32|  2.19M|    }
   33|       |    // 0xd800 .. 0xdbff - low surrogate
   34|       |    // 0xdc00 .. 0xdfff - high surrogate
   35|  2.19M|    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.19M|    const auto c0 = min(input & uint16_t(0xff80), one);
   39|       |
   40|       |    // c1 - chars that yield 3-byte UTF-8 codes (including surrogates)
   41|  2.19M|    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.19M|    v_count += c0;
   68|  2.19M|    v_count += c1;
   69|  2.19M|    v_count += vector_u16(is_surrogate);
   70|       |
   71|  2.19M|    iteration -= 1;
   72|  2.19M|    if (iteration == 0) {
  ------------------
  |  Branch (72:9): [True: 49, False: 2.19M]
  ------------------
   73|     49|      count += v_count.sum();
   74|     49|      v_count = vector_u16::zero();
   75|     49|      iteration = max_iterations;
   76|     49|    }
   77|  2.19M|  }
   78|       |
   79|    741|  if (iteration > 0) {
  ------------------
  |  Branch (79:7): [True: 741, False: 0]
  ------------------
   80|    741|    count += v_count.sum();
   81|    741|  }
   82|       |
   83|    741|  return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos,
   84|    741|                                                                   size - pos);
   85|    741|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf3222utf8_length_from_utf32EPKDim:
   11|    884|                                                    size_t length) {
   12|    884|  using vector_u32 = simd32<uint32_t>;
   13|       |
   14|    884|  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|    884|  const size_t max_increment = 3;
   19|       |
   20|    884|  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|    884|  const auto v_ffffff80 = vector_u32::splat(0xffffff80);
   28|    884|  const auto v_fffff800 = vector_u32::splat(0xfffff800);
   29|    884|  const auto v_ffff0000 = vector_u32::splat(0xffff0000);
   30|    884|  const auto one = vector_u32::splat(1);
   31|    884|#endif // SIMDUTF_SIMD_HAS_UNSIGNED_CMP
   32|       |
   33|    884|  size_t counter = 0;
   34|       |
   35|       |  // 1. vectorized loop unrolled 4 times
   36|    884|  {
   37|       |    // we use vector of uint32 counters, this is why this limit is used
   38|    884|    const size_t max_iterations =
   39|    884|        std::numeric_limits<uint32_t>::max() / (max_increment * 4);
   40|    884|    size_t blocks = length / (N * 4);
   41|    884|    length -= blocks * (N * 4);
   42|  1.09k|    while (blocks != 0) {
  ------------------
  |  Branch (42:12): [True: 209, False: 884]
  ------------------
   43|    209|      const size_t iterations = min(blocks, max_iterations);
   44|    209|      blocks -= iterations;
   45|       |
   46|    209|      simd32<uint32_t> acc = vector_u32::zero();
   47|  82.7k|      for (size_t i = 0; i < iterations; i++) {
  ------------------
  |  Branch (47:26): [True: 82.5k, False: 209]
  ------------------
   48|  82.5k|        const auto in0 = vector_u32(input + 0 * N);
   49|  82.5k|        const auto in1 = vector_u32(input + 1 * N);
   50|  82.5k|        const auto in2 = vector_u32(input + 2 * N);
   51|  82.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|  82.5k|        acc += min(one, in0 & v_ffffff80);
   70|  82.5k|        acc += min(one, in1 & v_ffffff80);
   71|  82.5k|        acc += min(one, in2 & v_ffffff80);
   72|  82.5k|        acc += min(one, in3 & v_ffffff80);
   73|       |
   74|  82.5k|        acc += min(one, in0 & v_fffff800);
   75|  82.5k|        acc += min(one, in1 & v_fffff800);
   76|  82.5k|        acc += min(one, in2 & v_fffff800);
   77|  82.5k|        acc += min(one, in3 & v_fffff800);
   78|       |
   79|  82.5k|        acc += min(one, in0 & v_ffff0000);
   80|  82.5k|        acc += min(one, in1 & v_ffff0000);
   81|  82.5k|        acc += min(one, in2 & v_ffff0000);
   82|  82.5k|        acc += min(one, in3 & v_ffff0000);
   83|  82.5k|#endif // SIMDUTF_SIMD_HAS_UNSIGNED_CMP
   84|       |
   85|  82.5k|        input += 4 * N;
   86|  82.5k|      }
   87|       |
   88|    209|      counter += acc.sum();
   89|    209|    }
   90|    884|  }
   91|       |
   92|       |  // 2. vectorized loop for tail
   93|    884|  {
   94|    884|    const size_t max_iterations =
   95|    884|        std::numeric_limits<uint32_t>::max() / max_increment;
   96|    884|    size_t blocks = length / N;
   97|    884|    length -= blocks * N;
   98|  1.33k|    while (blocks != 0) {
  ------------------
  |  Branch (98:12): [True: 450, False: 884]
  ------------------
   99|    450|      const size_t iterations = min(blocks, max_iterations);
  100|    450|      blocks -= iterations;
  101|       |
  102|    450|      auto acc = vector_u32::zero();
  103|  1.55k|      for (size_t i = 0; i < iterations; i++) {
  ------------------
  |  Branch (103:26): [True: 1.10k, False: 450]
  ------------------
  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|    450|      counter += acc.sum();
  120|    450|    }
  121|    884|  }
  122|       |
  123|    884|  const size_t consumed = input - start;
  124|    884|  if (consumed != 0) {
  ------------------
  |  Branch (124:7): [True: 496, False: 388]
  ------------------
  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|    496|    counter += consumed;
  128|    496|  }
  129|       |
  130|    884|  return counter + scalar::utf32::utf8_length_from_utf32(input, length);
  131|    884|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf323minImEET_S4_S4_:
    8|    659|template <typename T> T min(T a, T b) { return a <= b ? a : b; }
  ------------------
  |  Branch (8:48): [True: 659, False: 0]
  ------------------
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf3222utf8_length_from_utf32EPKDim:
   11|    884|                                                    size_t length) {
   12|    884|  using vector_u32 = simd32<uint32_t>;
   13|       |
   14|    884|  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|    884|  const size_t max_increment = 3;
   19|       |
   20|    884|  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|    884|  const auto v_ffffff80 = vector_u32::splat(0xffffff80);
   28|    884|  const auto v_fffff800 = vector_u32::splat(0xfffff800);
   29|    884|  const auto v_ffff0000 = vector_u32::splat(0xffff0000);
   30|    884|  const auto one = vector_u32::splat(1);
   31|    884|#endif // SIMDUTF_SIMD_HAS_UNSIGNED_CMP
   32|       |
   33|    884|  size_t counter = 0;
   34|       |
   35|       |  // 1. vectorized loop unrolled 4 times
   36|    884|  {
   37|       |    // we use vector of uint32 counters, this is why this limit is used
   38|    884|    const size_t max_iterations =
   39|    884|        std::numeric_limits<uint32_t>::max() / (max_increment * 4);
   40|    884|    size_t blocks = length / (N * 4);
   41|    884|    length -= blocks * (N * 4);
   42|  1.35k|    while (blocks != 0) {
  ------------------
  |  Branch (42:12): [True: 470, False: 884]
  ------------------
   43|    470|      const size_t iterations = min(blocks, max_iterations);
   44|    470|      blocks -= iterations;
   45|       |
   46|    470|      simd32<uint32_t> acc = vector_u32::zero();
   47|   165k|      for (size_t i = 0; i < iterations; i++) {
  ------------------
  |  Branch (47:26): [True: 165k, False: 470]
  ------------------
   48|   165k|        const auto in0 = vector_u32(input + 0 * N);
   49|   165k|        const auto in1 = vector_u32(input + 1 * N);
   50|   165k|        const auto in2 = vector_u32(input + 2 * N);
   51|   165k|        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|   165k|        acc += min(one, in0 & v_ffffff80);
   70|   165k|        acc += min(one, in1 & v_ffffff80);
   71|   165k|        acc += min(one, in2 & v_ffffff80);
   72|   165k|        acc += min(one, in3 & v_ffffff80);
   73|       |
   74|   165k|        acc += min(one, in0 & v_fffff800);
   75|   165k|        acc += min(one, in1 & v_fffff800);
   76|   165k|        acc += min(one, in2 & v_fffff800);
   77|   165k|        acc += min(one, in3 & v_fffff800);
   78|       |
   79|   165k|        acc += min(one, in0 & v_ffff0000);
   80|   165k|        acc += min(one, in1 & v_ffff0000);
   81|   165k|        acc += min(one, in2 & v_ffff0000);
   82|   165k|        acc += min(one, in3 & v_ffff0000);
   83|   165k|#endif // SIMDUTF_SIMD_HAS_UNSIGNED_CMP
   84|       |
   85|   165k|        input += 4 * N;
   86|   165k|      }
   87|       |
   88|    470|      counter += acc.sum();
   89|    470|    }
   90|    884|  }
   91|       |
   92|       |  // 2. vectorized loop for tail
   93|    884|  {
   94|    884|    const size_t max_iterations =
   95|    884|        std::numeric_limits<uint32_t>::max() / max_increment;
   96|    884|    size_t blocks = length / N;
   97|    884|    length -= blocks * N;
   98|  1.36k|    while (blocks != 0) {
  ------------------
  |  Branch (98:12): [True: 478, False: 884]
  ------------------
   99|    478|      const size_t iterations = min(blocks, max_iterations);
  100|    478|      blocks -= iterations;
  101|       |
  102|    478|      auto acc = vector_u32::zero();
  103|  1.66k|      for (size_t i = 0; i < iterations; i++) {
  ------------------
  |  Branch (103:26): [True: 1.18k, False: 478]
  ------------------
  104|  1.18k|        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.18k|        acc += min(one, in & v_ffffff80);
  112|  1.18k|        acc += min(one, in & v_fffff800);
  113|  1.18k|        acc += min(one, in & v_ffff0000);
  114|  1.18k|#endif // SIMDUTF_SIMD_HAS_UNSIGNED_CMP
  115|       |
  116|  1.18k|        input += N;
  117|  1.18k|      }
  118|       |
  119|    478|      counter += acc.sum();
  120|    478|    }
  121|    884|  }
  122|       |
  123|    884|  const size_t consumed = input - start;
  124|    884|  if (consumed != 0) {
  ------------------
  |  Branch (124:7): [True: 529, 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|    529|    counter += consumed;
  128|    529|  }
  129|       |
  130|    884|  return counter + scalar::utf32::utf8_length_from_utf32(input, length);
  131|    884|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf323minImEET_S4_S4_:
    8|    948|template <typename T> T min(T a, T b) { return a <= b ? a : b; }
  ------------------
  |  Branch (8:48): [True: 948, False: 0]
  ------------------

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

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

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_114utf8_to_latin121validating_transcoderC2Ev:
  101|  1.07k|  validating_transcoder() : error(uint8_t(0)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_114utf8_to_latin121validating_transcoder7convertEPKcmPc:
  115|    576|                                       char *latin1_output) {
  116|    576|    size_t pos = 0;
  117|    576|    char *start{latin1_output};
  118|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  119|       |    // of 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the
  120|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  121|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  122|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  123|       |    // back from the end counting 16 leading bytes, to give us a good margin.
  124|    576|    size_t leading_byte = 0;
  125|    576|    size_t margin = size;
  126|  10.2k|    for (; margin > 0 && leading_byte < 16; margin--) {
  ------------------
  |  Branch (126:12): [True: 10.1k, False: 134]
  |  Branch (126:26): [True: 9.67k, False: 442]
  ------------------
  127|  9.67k|      leading_byte += (int8_t(in[margin - 1]) >
  128|  9.67k|                       -65); // twos complement of -65 is 1011 1111 ...
  129|  9.67k|    }
  130|       |    // If the input is long enough, then we have that margin-1 is the eight last
  131|       |    // leading byte.
  132|    576|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  133|   670k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (133:12): [True: 670k, False: 550]
  ------------------
  134|   670k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  135|   670k|      if (input.is_ascii()) {
  ------------------
  |  Branch (135:11): [True: 619k, False: 50.7k]
  ------------------
  136|   619k|        input.store((int8_t *)latin1_output);
  137|   619k|        latin1_output += 64;
  138|   619k|        pos += 64;
  139|   619k|      } else {
  140|       |        // you might think that a for-loop would work, but under Visual Studio,
  141|       |        // it is not good enough.
  142|  50.7k|        static_assert(
  143|  50.7k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  144|  50.7k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  145|  50.7k|            "We support either two or four chunks per 64-byte block.");
  146|  50.7k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  147|  50.7k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  148|  50.7k|          this->check_utf8_bytes(input.chunks[0], zero);
  149|  50.7k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  150|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  151|       |          this->check_utf8_bytes(input.chunks[0], zero);
  152|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  153|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  154|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  155|       |        }
  156|  50.7k|        uint64_t utf8_continuation_mask =
  157|  50.7k|            input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in
  158|       |                               // this case, we also have ASCII to account for.
  159|  50.7k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (159:13): [True: 26, False: 50.7k]
  ------------------
  160|     26|          return 0; // error
  161|     26|        }
  162|  50.7k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  163|  50.7k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  164|       |        // We process in blocks of up to 12 bytes except possibly
  165|       |        // for fast paths which may process up to 16 bytes. For the
  166|       |        // slow path to work, we should have at least 12 input bytes left.
  167|  50.7k|        size_t max_starting_point = (pos + 64) - 12;
  168|       |        // Next loop is going to run at least five times.
  169|   502k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (169:16): [True: 451k, False: 50.7k]
  ------------------
  170|       |          // Performance note: our ability to compute 'consumed' and
  171|       |          // then shift and recompute is critical. If there is a
  172|       |          // latency of, say, 4 cycles on getting 'consumed', then
  173|       |          // the inner loop might have a total latency of about 6 cycles.
  174|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  175|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  176|       |          // for this section of the code. Hence, there is a limit
  177|       |          // to how much we can further increase this latency before
  178|       |          // it seriously harms performance.
  179|   451k|          size_t consumed = convert_masked_utf8_to_latin1(
  180|   451k|              in + pos, utf8_end_of_code_point_mask, latin1_output);
  181|   451k|          pos += consumed;
  182|   451k|          utf8_end_of_code_point_mask >>= consumed;
  183|   451k|        }
  184|       |        // At this point there may remain between 0 and 12 bytes in the
  185|       |        // 64-byte block. These bytes will be processed again. So we have an
  186|       |        // 80% efficiency (in the worst case). In practice we expect an
  187|       |        // 85% to 90% efficiency.
  188|  50.7k|      }
  189|   670k|    }
  190|    550|    if (errors()) {
  ------------------
  |  Branch (190:9): [True: 238, False: 312]
  ------------------
  191|    238|      return 0;
  192|    238|    }
  193|    312|    if (pos < size) {
  ------------------
  |  Branch (193:9): [True: 308, False: 4]
  ------------------
  194|    308|      size_t howmany =
  195|    308|          scalar::utf8_to_latin1::convert(in + pos, size - pos, latin1_output);
  196|    308|      if (howmany == 0) {
  ------------------
  |  Branch (196:11): [True: 124, False: 184]
  ------------------
  197|    124|        return 0;
  198|    124|      }
  199|    184|      latin1_output += howmany;
  200|    184|    }
  201|    188|    return latin1_output - start;
  202|    312|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_114utf8_to_latin121validating_transcoder16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  106|   137k|                                              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|   137k|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  111|   137k|    this->error |= check_special_cases(input, prev1);
  112|   137k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_114utf8_to_latin119check_special_casesENS1_4simd5simd8IhEES5_:
    8|   137k|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|   137k|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   20|       |                                               // 11______ 11______
   21|   137k|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   22|   137k|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   23|   137k|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   24|   137k|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   25|   137k|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   26|   137k|  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|   137k|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   35|       |  // 11110101 1000____
   36|       |  // 1111011_ 1000____
   37|       |  // 11111___ 1000____
   38|   137k|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   39|   137k|  constexpr const uint8_t FORBIDDEN = 0xff;
   40|       |
   41|   137k|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   42|       |      // 0_______ ________ <ASCII in byte 1>
   43|   137k|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   44|   137k|      TOO_LONG,
   45|       |      // 10______ ________ <continuation in byte 1>
   46|   137k|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   47|       |      // 1100____ ________ <two byte lead in byte 1>
   48|   137k|      TOO_SHORT | OVERLONG_2,
   49|       |      // 1101____ ________ <two byte lead in byte 1>
   50|   137k|      FORBIDDEN,
   51|       |      // 1110____ ________ <three byte lead in byte 1>
   52|   137k|      FORBIDDEN,
   53|       |      // 1111____ ________ <four+ byte lead in byte 1>
   54|   137k|      FORBIDDEN);
   55|   137k|  constexpr const uint8_t CARRY =
   56|   137k|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   57|   137k|  const simd8<uint8_t> byte_1_low =
   58|   137k|      (prev1 & 0x0F)
   59|   137k|          .lookup_16<uint8_t>(
   60|       |              // ____0000 ________
   61|   137k|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   62|       |              // ____0001 ________
   63|   137k|              CARRY | OVERLONG_2,
   64|       |              // ____001_ ________
   65|   137k|              CARRY, CARRY,
   66|       |
   67|       |              // ____0100 ________
   68|   137k|              FORBIDDEN,
   69|       |              // ____0101 ________
   70|   137k|              FORBIDDEN,
   71|       |              // ____011_ ________
   72|   137k|              FORBIDDEN, FORBIDDEN,
   73|       |
   74|       |              // ____1___ ________
   75|   137k|              FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN,
   76|       |              // ____1101 ________
   77|   137k|              FORBIDDEN, FORBIDDEN, FORBIDDEN);
   78|   137k|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   79|       |      // ________ 0_______ <ASCII in byte 2>
   80|   137k|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   81|   137k|      TOO_SHORT, TOO_SHORT,
   82|       |
   83|       |      // ________ 1000____
   84|   137k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   85|   137k|          OVERLONG_4,
   86|       |      // ________ 1001____
   87|   137k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   88|       |      // ________ 101_____
   89|   137k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   90|   137k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   91|       |
   92|       |      // ________ 11______
   93|   137k|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   94|   137k|  return (byte_1_high & byte_1_low & byte_2_high);
   95|   137k|}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_114utf8_to_latin121validating_transcoder6errorsEv:
  308|  19.1k|  simdutf_really_inline bool errors() const {
  309|  19.1k|    return this->error.any_bits_set_anywhere();
  310|  19.1k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_114utf8_to_latin121validating_transcoder19convert_with_errorsEPKcmPc:
  205|    500|                                                   char *latin1_output) {
  206|    500|    size_t pos = 0;
  207|    500|    char *start{latin1_output};
  208|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  209|       |    // of 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the
  210|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  211|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  212|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  213|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  214|    500|    size_t leading_byte = 0;
  215|    500|    size_t margin = size;
  216|  4.51k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (216:12): [True: 4.39k, False: 118]
  |  Branch (216:26): [True: 4.01k, False: 382]
  ------------------
  217|  4.01k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  218|  4.01k|    }
  219|       |    // If the input is long enough, then we have that margin-1 is the eight last
  220|       |    // leading byte.
  221|    500|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  222|   349k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (222:12): [True: 348k, False: 432]
  ------------------
  223|   348k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  224|   348k|      if (input.is_ascii()) {
  ------------------
  |  Branch (224:11): [True: 330k, False: 18.1k]
  ------------------
  225|   330k|        input.store((int8_t *)latin1_output);
  226|   330k|        latin1_output += 64;
  227|   330k|        pos += 64;
  228|   330k|      } else {
  229|       |        // you might think that a for-loop would work, but under Visual Studio,
  230|       |        // it is not good enough.
  231|  18.1k|        static_assert(
  232|  18.1k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  233|  18.1k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  234|  18.1k|            "We support either two or four chunks per 64-byte block.");
  235|  18.1k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  236|  18.1k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  237|  18.1k|          this->check_utf8_bytes(input.chunks[0], zero);
  238|  18.1k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  239|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  240|       |          this->check_utf8_bytes(input.chunks[0], zero);
  241|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  242|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  243|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  244|       |        }
  245|  18.1k|        if (errors()) {
  ------------------
  |  Branch (245:13): [True: 68, False: 18.0k]
  ------------------
  246|       |          // rewind_and_convert_with_errors will seek a potential error from
  247|       |          // in+pos onward, with the ability to go back up to pos bytes, and
  248|       |          // read size-pos bytes forward.
  249|     68|          result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(
  250|     68|              pos, in + pos, size - pos, latin1_output);
  251|     68|          res.count += pos;
  252|     68|          return res;
  253|     68|        }
  254|  18.0k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  255|  18.0k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  256|  18.0k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  257|       |        // We process in blocks of up to 12 bytes except possibly
  258|       |        // for fast paths which may process up to 16 bytes. For the
  259|       |        // slow path to work, we should have at least 12 input bytes left.
  260|  18.0k|        size_t max_starting_point = (pos + 64) - 12;
  261|       |        // Next loop is going to run at least five times.
  262|   180k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (262:16): [True: 162k, False: 18.0k]
  ------------------
  263|       |          // Performance note: our ability to compute 'consumed' and
  264|       |          // then shift and recompute is critical. If there is a
  265|       |          // latency of, say, 4 cycles on getting 'consumed', then
  266|       |          // the inner loop might have a total latency of about 6 cycles.
  267|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  268|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  269|       |          // for this section of the code. Hence, there is a limit
  270|       |          // to how much we can further increase this latency before
  271|       |          // it seriously harms performance.
  272|   162k|          size_t consumed = convert_masked_utf8_to_latin1(
  273|   162k|              in + pos, utf8_end_of_code_point_mask, latin1_output);
  274|   162k|          pos += consumed;
  275|   162k|          utf8_end_of_code_point_mask >>= consumed;
  276|   162k|        }
  277|       |        // At this point there may remain between 0 and 12 bytes in the
  278|       |        // 64-byte block. These bytes will be processed again. So we have an
  279|       |        // 80% efficiency (in the worst case). In practice we expect an
  280|       |        // 85% to 90% efficiency.
  281|  18.0k|      }
  282|   348k|    }
  283|    432|    if (errors()) {
  ------------------
  |  Branch (283:9): [True: 0, False: 432]
  ------------------
  284|       |      // rewind_and_convert_with_errors will seek a potential error from in+pos
  285|       |      // onward, with the ability to go back up to pos bytes, and read size-pos
  286|       |      // bytes forward.
  287|      0|      result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(
  288|      0|          pos, in + pos, size - pos, latin1_output);
  289|      0|      res.count += pos;
  290|      0|      return res;
  291|      0|    }
  292|    432|    if (pos < size) {
  ------------------
  |  Branch (292:9): [True: 428, False: 4]
  ------------------
  293|       |      // rewind_and_convert_with_errors will seek a potential error from in+pos
  294|       |      // onward, with the ability to go back up to pos bytes, and read size-pos
  295|       |      // bytes forward.
  296|    428|      result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(
  297|    428|          pos, in + pos, size - pos, latin1_output);
  298|    428|      if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (298:11): [True: 202, False: 226]
  ------------------
  299|    202|        res.count += pos;
  300|    202|        return res;
  301|    226|      } else { // In case of success, we want the number of word written
  302|    226|        latin1_output += res.count;
  303|    226|      }
  304|    428|    }
  305|    230|    return result(error_code::SUCCESS, latin1_output - start);
  306|    432|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_114utf8_to_latin121validating_transcoderC2Ev:
  101|  1.07k|  validating_transcoder() : error(uint8_t(0)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_114utf8_to_latin121validating_transcoder7convertEPKcmPc:
  115|    576|                                       char *latin1_output) {
  116|    576|    size_t pos = 0;
  117|    576|    char *start{latin1_output};
  118|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  119|       |    // of 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the
  120|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  121|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  122|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  123|       |    // back from the end counting 16 leading bytes, to give us a good margin.
  124|    576|    size_t leading_byte = 0;
  125|    576|    size_t margin = size;
  126|  10.2k|    for (; margin > 0 && leading_byte < 16; margin--) {
  ------------------
  |  Branch (126:12): [True: 10.1k, False: 134]
  |  Branch (126:26): [True: 9.67k, False: 442]
  ------------------
  127|  9.67k|      leading_byte += (int8_t(in[margin - 1]) >
  128|  9.67k|                       -65); // twos complement of -65 is 1011 1111 ...
  129|  9.67k|    }
  130|       |    // If the input is long enough, then we have that margin-1 is the eight last
  131|       |    // leading byte.
  132|    576|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  133|   670k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (133:12): [True: 670k, False: 550]
  ------------------
  134|   670k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  135|   670k|      if (input.is_ascii()) {
  ------------------
  |  Branch (135:11): [True: 619k, False: 50.7k]
  ------------------
  136|   619k|        input.store((int8_t *)latin1_output);
  137|   619k|        latin1_output += 64;
  138|   619k|        pos += 64;
  139|   619k|      } else {
  140|       |        // you might think that a for-loop would work, but under Visual Studio,
  141|       |        // it is not good enough.
  142|  50.7k|        static_assert(
  143|  50.7k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  144|  50.7k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  145|  50.7k|            "We support either two or four chunks per 64-byte block.");
  146|  50.7k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  147|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  148|       |          this->check_utf8_bytes(input.chunks[0], zero);
  149|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  150|  50.7k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  151|  50.7k|          this->check_utf8_bytes(input.chunks[0], zero);
  152|  50.7k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  153|  50.7k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  154|  50.7k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  155|  50.7k|        }
  156|  50.7k|        uint64_t utf8_continuation_mask =
  157|  50.7k|            input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in
  158|       |                               // this case, we also have ASCII to account for.
  159|  50.7k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (159:13): [True: 26, False: 50.7k]
  ------------------
  160|     26|          return 0; // error
  161|     26|        }
  162|  50.7k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  163|  50.7k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  164|       |        // We process in blocks of up to 12 bytes except possibly
  165|       |        // for fast paths which may process up to 16 bytes. For the
  166|       |        // slow path to work, we should have at least 12 input bytes left.
  167|  50.7k|        size_t max_starting_point = (pos + 64) - 12;
  168|       |        // Next loop is going to run at least five times.
  169|   502k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (169:16): [True: 451k, False: 50.7k]
  ------------------
  170|       |          // Performance note: our ability to compute 'consumed' and
  171|       |          // then shift and recompute is critical. If there is a
  172|       |          // latency of, say, 4 cycles on getting 'consumed', then
  173|       |          // the inner loop might have a total latency of about 6 cycles.
  174|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  175|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  176|       |          // for this section of the code. Hence, there is a limit
  177|       |          // to how much we can further increase this latency before
  178|       |          // it seriously harms performance.
  179|   451k|          size_t consumed = convert_masked_utf8_to_latin1(
  180|   451k|              in + pos, utf8_end_of_code_point_mask, latin1_output);
  181|   451k|          pos += consumed;
  182|   451k|          utf8_end_of_code_point_mask >>= consumed;
  183|   451k|        }
  184|       |        // At this point there may remain between 0 and 12 bytes in the
  185|       |        // 64-byte block. These bytes will be processed again. So we have an
  186|       |        // 80% efficiency (in the worst case). In practice we expect an
  187|       |        // 85% to 90% efficiency.
  188|  50.7k|      }
  189|   670k|    }
  190|    550|    if (errors()) {
  ------------------
  |  Branch (190:9): [True: 238, False: 312]
  ------------------
  191|    238|      return 0;
  192|    238|    }
  193|    312|    if (pos < size) {
  ------------------
  |  Branch (193:9): [True: 308, False: 4]
  ------------------
  194|    308|      size_t howmany =
  195|    308|          scalar::utf8_to_latin1::convert(in + pos, size - pos, latin1_output);
  196|    308|      if (howmany == 0) {
  ------------------
  |  Branch (196:11): [True: 124, False: 184]
  ------------------
  197|    124|        return 0;
  198|    124|      }
  199|    184|      latin1_output += howmany;
  200|    184|    }
  201|    188|    return latin1_output - start;
  202|    312|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_114utf8_to_latin121validating_transcoder16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  106|   275k|                                              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|   275k|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  111|   275k|    this->error |= check_special_cases(input, prev1);
  112|   275k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_114utf8_to_latin119check_special_casesENS1_4simd5simd8IhEES5_:
    8|   275k|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|   275k|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   20|       |                                               // 11______ 11______
   21|   275k|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   22|   275k|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   23|   275k|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   24|   275k|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   25|   275k|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   26|   275k|  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|   275k|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   35|       |  // 11110101 1000____
   36|       |  // 1111011_ 1000____
   37|       |  // 11111___ 1000____
   38|   275k|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   39|   275k|  constexpr const uint8_t FORBIDDEN = 0xff;
   40|       |
   41|   275k|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   42|       |      // 0_______ ________ <ASCII in byte 1>
   43|   275k|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   44|   275k|      TOO_LONG,
   45|       |      // 10______ ________ <continuation in byte 1>
   46|   275k|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   47|       |      // 1100____ ________ <two byte lead in byte 1>
   48|   275k|      TOO_SHORT | OVERLONG_2,
   49|       |      // 1101____ ________ <two byte lead in byte 1>
   50|   275k|      FORBIDDEN,
   51|       |      // 1110____ ________ <three byte lead in byte 1>
   52|   275k|      FORBIDDEN,
   53|       |      // 1111____ ________ <four+ byte lead in byte 1>
   54|   275k|      FORBIDDEN);
   55|   275k|  constexpr const uint8_t CARRY =
   56|   275k|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   57|   275k|  const simd8<uint8_t> byte_1_low =
   58|   275k|      (prev1 & 0x0F)
   59|   275k|          .lookup_16<uint8_t>(
   60|       |              // ____0000 ________
   61|   275k|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   62|       |              // ____0001 ________
   63|   275k|              CARRY | OVERLONG_2,
   64|       |              // ____001_ ________
   65|   275k|              CARRY, CARRY,
   66|       |
   67|       |              // ____0100 ________
   68|   275k|              FORBIDDEN,
   69|       |              // ____0101 ________
   70|   275k|              FORBIDDEN,
   71|       |              // ____011_ ________
   72|   275k|              FORBIDDEN, FORBIDDEN,
   73|       |
   74|       |              // ____1___ ________
   75|   275k|              FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN, FORBIDDEN,
   76|       |              // ____1101 ________
   77|   275k|              FORBIDDEN, FORBIDDEN, FORBIDDEN);
   78|   275k|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   79|       |      // ________ 0_______ <ASCII in byte 2>
   80|   275k|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   81|   275k|      TOO_SHORT, TOO_SHORT,
   82|       |
   83|       |      // ________ 1000____
   84|   275k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   85|   275k|          OVERLONG_4,
   86|       |      // ________ 1001____
   87|   275k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   88|       |      // ________ 101_____
   89|   275k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   90|   275k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   91|       |
   92|       |      // ________ 11______
   93|   275k|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   94|   275k|  return (byte_1_high & byte_1_low & byte_2_high);
   95|   275k|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_114utf8_to_latin121validating_transcoder6errorsEv:
  308|  19.1k|  simdutf_really_inline bool errors() const {
  309|  19.1k|    return this->error.any_bits_set_anywhere();
  310|  19.1k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_114utf8_to_latin121validating_transcoder19convert_with_errorsEPKcmPc:
  205|    500|                                                   char *latin1_output) {
  206|    500|    size_t pos = 0;
  207|    500|    char *start{latin1_output};
  208|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  209|       |    // of 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the
  210|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  211|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  212|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  213|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  214|    500|    size_t leading_byte = 0;
  215|    500|    size_t margin = size;
  216|  4.51k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (216:12): [True: 4.39k, False: 118]
  |  Branch (216:26): [True: 4.01k, False: 382]
  ------------------
  217|  4.01k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  218|  4.01k|    }
  219|       |    // If the input is long enough, then we have that margin-1 is the eight last
  220|       |    // leading byte.
  221|    500|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  222|   349k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (222:12): [True: 348k, False: 432]
  ------------------
  223|   348k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  224|   348k|      if (input.is_ascii()) {
  ------------------
  |  Branch (224:11): [True: 330k, False: 18.1k]
  ------------------
  225|   330k|        input.store((int8_t *)latin1_output);
  226|   330k|        latin1_output += 64;
  227|   330k|        pos += 64;
  228|   330k|      } else {
  229|       |        // you might think that a for-loop would work, but under Visual Studio,
  230|       |        // it is not good enough.
  231|  18.1k|        static_assert(
  232|  18.1k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  233|  18.1k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  234|  18.1k|            "We support either two or four chunks per 64-byte block.");
  235|  18.1k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  236|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  237|       |          this->check_utf8_bytes(input.chunks[0], zero);
  238|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  239|  18.1k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  240|  18.1k|          this->check_utf8_bytes(input.chunks[0], zero);
  241|  18.1k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  242|  18.1k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  243|  18.1k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  244|  18.1k|        }
  245|  18.1k|        if (errors()) {
  ------------------
  |  Branch (245:13): [True: 68, False: 18.0k]
  ------------------
  246|       |          // rewind_and_convert_with_errors will seek a potential error from
  247|       |          // in+pos onward, with the ability to go back up to pos bytes, and
  248|       |          // read size-pos bytes forward.
  249|     68|          result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(
  250|     68|              pos, in + pos, size - pos, latin1_output);
  251|     68|          res.count += pos;
  252|     68|          return res;
  253|     68|        }
  254|  18.0k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  255|  18.0k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  256|  18.0k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  257|       |        // We process in blocks of up to 12 bytes except possibly
  258|       |        // for fast paths which may process up to 16 bytes. For the
  259|       |        // slow path to work, we should have at least 12 input bytes left.
  260|  18.0k|        size_t max_starting_point = (pos + 64) - 12;
  261|       |        // Next loop is going to run at least five times.
  262|   180k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (262:16): [True: 162k, False: 18.0k]
  ------------------
  263|       |          // Performance note: our ability to compute 'consumed' and
  264|       |          // then shift and recompute is critical. If there is a
  265|       |          // latency of, say, 4 cycles on getting 'consumed', then
  266|       |          // the inner loop might have a total latency of about 6 cycles.
  267|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  268|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  269|       |          // for this section of the code. Hence, there is a limit
  270|       |          // to how much we can further increase this latency before
  271|       |          // it seriously harms performance.
  272|   162k|          size_t consumed = convert_masked_utf8_to_latin1(
  273|   162k|              in + pos, utf8_end_of_code_point_mask, latin1_output);
  274|   162k|          pos += consumed;
  275|   162k|          utf8_end_of_code_point_mask >>= consumed;
  276|   162k|        }
  277|       |        // At this point there may remain between 0 and 12 bytes in the
  278|       |        // 64-byte block. These bytes will be processed again. So we have an
  279|       |        // 80% efficiency (in the worst case). In practice we expect an
  280|       |        // 85% to 90% efficiency.
  281|  18.0k|      }
  282|   348k|    }
  283|    432|    if (errors()) {
  ------------------
  |  Branch (283:9): [True: 0, False: 432]
  ------------------
  284|       |      // rewind_and_convert_with_errors will seek a potential error from in+pos
  285|       |      // onward, with the ability to go back up to pos bytes, and read size-pos
  286|       |      // bytes forward.
  287|      0|      result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(
  288|      0|          pos, in + pos, size - pos, latin1_output);
  289|      0|      res.count += pos;
  290|      0|      return res;
  291|      0|    }
  292|    432|    if (pos < size) {
  ------------------
  |  Branch (292:9): [True: 428, False: 4]
  ------------------
  293|       |      // rewind_and_convert_with_errors will seek a potential error from in+pos
  294|       |      // onward, with the ability to go back up to pos bytes, and read size-pos
  295|       |      // bytes forward.
  296|    428|      result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(
  297|    428|          pos, in + pos, size - pos, latin1_output);
  298|    428|      if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (298:11): [True: 202, False: 226]
  ------------------
  299|    202|        res.count += pos;
  300|    202|        return res;
  301|    226|      } else { // In case of success, we want the number of word written
  302|    226|        latin1_output += res.count;
  303|    226|      }
  304|    428|    }
  305|    230|    return result(error_code::SUCCESS, latin1_output - start);
  306|    432|  }

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoderC2Ev:
  114|  2.83k|  validating_transcoder() : error(uint8_t(0)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoderC2Ev:
  114|  2.83k|  validating_transcoder() : error(uint8_t(0)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoder7convertILNS_10endiannessE0EEEmPKcmPDs:
  130|    754|                                       char16_t *utf16_output) {
  131|    754|    size_t pos = 0;
  132|    754|    char16_t *start{utf16_output};
  133|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  134|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  135|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  136|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  137|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  138|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  139|    754|    size_t leading_byte = 0;
  140|    754|    size_t margin = size;
  141|  7.21k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (141:12): [True: 6.96k, False: 246]
  |  Branch (141:26): [True: 6.45k, False: 508]
  ------------------
  142|  6.45k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  143|  6.45k|    }
  144|       |    // If the input is long enough, then we have that margin-1 is the eight last
  145|       |    // leading byte.
  146|    754|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  147|   564k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (147:12): [True: 563k, False: 732]
  ------------------
  148|   563k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  149|   563k|      if (input.is_ascii()) {
  ------------------
  |  Branch (149:11): [True: 466k, False: 97.6k]
  ------------------
  150|   466k|        input.store_ascii_as_utf16<endian>(utf16_output);
  151|   466k|        utf16_output += 64;
  152|   466k|        pos += 64;
  153|   466k|      } else {
  154|       |        // you might think that a for-loop would work, but under Visual Studio,
  155|       |        // it is not good enough.
  156|  97.6k|        static_assert(
  157|  97.6k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  158|  97.6k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  159|  97.6k|            "We support either two or four chunks per 64-byte block.");
  160|  97.6k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  161|  97.6k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  162|  97.6k|          this->check_utf8_bytes(input.chunks[0], zero);
  163|  97.6k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  164|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  165|       |          this->check_utf8_bytes(input.chunks[0], zero);
  166|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  167|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  168|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  169|       |        }
  170|  97.6k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  171|  97.6k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (171:13): [True: 22, False: 97.5k]
  ------------------
  172|     22|          return 0; // error
  173|     22|        }
  174|  97.5k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  175|  97.5k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  176|       |        // We process in blocks of up to 12 bytes except possibly
  177|       |        // for fast paths which may process up to 16 bytes. For the
  178|       |        // slow path to work, we should have at least 12 input bytes left.
  179|  97.5k|        size_t max_starting_point = (pos + 64) - 12;
  180|       |        // Next loop is going to run at least five times.
  181|   948k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (181:16): [True: 850k, False: 97.5k]
  ------------------
  182|       |          // Performance note: our ability to compute 'consumed' and
  183|       |          // then shift and recompute is critical. If there is a
  184|       |          // latency of, say, 4 cycles on getting 'consumed', then
  185|       |          // the inner loop might have a total latency of about 6 cycles.
  186|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  187|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  188|       |          // for this section of the code. Hence, there is a limit
  189|       |          // to how much we can further increase this latency before
  190|       |          // it seriously harms performance.
  191|   850k|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  192|   850k|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  193|   850k|          pos += consumed;
  194|   850k|          utf8_end_of_code_point_mask >>= consumed;
  195|   850k|        }
  196|       |        // At this point there may remain between 0 and 12 bytes in the
  197|       |        // 64-byte block. These bytes will be processed again. So we have an
  198|       |        // 80% efficiency (in the worst case). In practice we expect an
  199|       |        // 85% to 90% efficiency.
  200|  97.5k|      }
  201|   563k|    }
  202|    732|    if (errors()) {
  ------------------
  |  Branch (202:9): [True: 292, False: 440]
  ------------------
  203|    292|      return 0;
  204|    292|    }
  205|    440|    if (pos < size) {
  ------------------
  |  Branch (205:9): [True: 436, False: 4]
  ------------------
  206|    436|      size_t howmany = scalar::utf8_to_utf16::convert<endian>(
  207|    436|          in + pos, size - pos, utf16_output);
  208|    436|      if (howmany == 0) {
  ------------------
  |  Branch (208:11): [True: 204, False: 232]
  ------------------
  209|    204|        return 0;
  210|    204|      }
  211|    232|      utf16_output += howmany;
  212|    232|    }
  213|    236|    return utf16_output - start;
  214|    440|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoder16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  119|   987k|                                              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|   987k|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  124|   987k|    simd8<uint8_t> sc = check_special_cases(input, prev1);
  125|   987k|    this->error |= check_multibyte_lengths(input, prev_input, sc);
  126|   987k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1619check_special_casesENS1_4simd5simd8IhEES5_:
    8|   987k|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|   987k|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   16|       |                                               // 11______ 11______
   17|   987k|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   18|   987k|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   19|   987k|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   20|   987k|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   21|   987k|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   22|   987k|  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|   987k|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   31|       |  // 11110101 1000____
   32|       |  // 1111011_ 1000____
   33|       |  // 11111___ 1000____
   34|   987k|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   35|       |
   36|   987k|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   37|       |      // 0_______ ________ <ASCII in byte 1>
   38|   987k|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   39|   987k|      TOO_LONG,
   40|       |      // 10______ ________ <continuation in byte 1>
   41|   987k|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   42|       |      // 1100____ ________ <two byte lead in byte 1>
   43|   987k|      TOO_SHORT | OVERLONG_2,
   44|       |      // 1101____ ________ <two byte lead in byte 1>
   45|   987k|      TOO_SHORT,
   46|       |      // 1110____ ________ <three byte lead in byte 1>
   47|   987k|      TOO_SHORT | OVERLONG_3 | SURROGATE,
   48|       |      // 1111____ ________ <four+ byte lead in byte 1>
   49|   987k|      TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4);
   50|   987k|  constexpr const uint8_t CARRY =
   51|   987k|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   52|   987k|  const simd8<uint8_t> byte_1_low =
   53|   987k|      (prev1 & 0x0F)
   54|   987k|          .lookup_16<uint8_t>(
   55|       |              // ____0000 ________
   56|   987k|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   57|       |              // ____0001 ________
   58|   987k|              CARRY | OVERLONG_2,
   59|       |              // ____001_ ________
   60|   987k|              CARRY, CARRY,
   61|       |
   62|       |              // ____0100 ________
   63|   987k|              CARRY | TOO_LARGE,
   64|       |              // ____0101 ________
   65|   987k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   66|       |              // ____011_ ________
   67|   987k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   68|   987k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   69|       |
   70|       |              // ____1___ ________
   71|   987k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   72|   987k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   73|   987k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   74|   987k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   75|   987k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   76|       |              // ____1101 ________
   77|   987k|              CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
   78|   987k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   79|   987k|              CARRY | TOO_LARGE | TOO_LARGE_1000);
   80|   987k|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   81|       |      // ________ 0_______ <ASCII in byte 2>
   82|   987k|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   83|   987k|      TOO_SHORT, TOO_SHORT,
   84|       |
   85|       |      // ________ 1000____
   86|   987k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   87|   987k|          OVERLONG_4,
   88|       |      // ________ 1001____
   89|   987k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   90|       |      // ________ 101_____
   91|   987k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   92|   987k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   93|       |
   94|       |      // ________ 11______
   95|   987k|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   96|   987k|  return (byte_1_high & byte_1_low & byte_2_high);
   97|   987k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1623check_multibyte_lengthsENS1_4simd5simd8IhEES5_S5_:
  101|   987k|                        const simd8<uint8_t> sc) {
  102|   987k|  simd8<uint8_t> prev2 = input.prev<2>(prev_input);
  103|   987k|  simd8<uint8_t> prev3 = input.prev<3>(prev_input);
  104|   987k|  simd8<uint8_t> must23 =
  105|   987k|      simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
  106|   987k|  simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
  107|   987k|  return must23_80 ^ sc;
  108|   987k|}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoder6errorsEv:
  324|   183k|  simdutf_really_inline bool errors() const {
  325|   183k|    return this->error.any_bits_set_anywhere();
  326|   183k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoder7convertILNS_10endiannessE1EEEmPKcmPDs:
  130|    758|                                       char16_t *utf16_output) {
  131|    758|    size_t pos = 0;
  132|    758|    char16_t *start{utf16_output};
  133|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  134|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  135|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  136|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  137|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  138|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  139|    758|    size_t leading_byte = 0;
  140|    758|    size_t margin = size;
  141|  7.45k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (141:12): [True: 7.22k, False: 226]
  |  Branch (141:26): [True: 6.69k, False: 532]
  ------------------
  142|  6.69k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  143|  6.69k|    }
  144|       |    // If the input is long enough, then we have that margin-1 is the eight last
  145|       |    // leading byte.
  146|    758|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  147|   521k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (147:12): [True: 521k, False: 726]
  ------------------
  148|   521k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  149|   521k|      if (input.is_ascii()) {
  ------------------
  |  Branch (149:11): [True: 305k, False: 215k]
  ------------------
  150|   305k|        input.store_ascii_as_utf16<endian>(utf16_output);
  151|   305k|        utf16_output += 64;
  152|   305k|        pos += 64;
  153|   305k|      } else {
  154|       |        // you might think that a for-loop would work, but under Visual Studio,
  155|       |        // it is not good enough.
  156|   215k|        static_assert(
  157|   215k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  158|   215k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  159|   215k|            "We support either two or four chunks per 64-byte block.");
  160|   215k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  161|   215k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  162|   215k|          this->check_utf8_bytes(input.chunks[0], zero);
  163|   215k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  164|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  165|       |          this->check_utf8_bytes(input.chunks[0], zero);
  166|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  167|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  168|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  169|       |        }
  170|   215k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  171|   215k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (171:13): [True: 32, False: 215k]
  ------------------
  172|     32|          return 0; // error
  173|     32|        }
  174|   215k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  175|   215k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  176|       |        // We process in blocks of up to 12 bytes except possibly
  177|       |        // for fast paths which may process up to 16 bytes. For the
  178|       |        // slow path to work, we should have at least 12 input bytes left.
  179|   215k|        size_t max_starting_point = (pos + 64) - 12;
  180|       |        // Next loop is going to run at least five times.
  181|  2.14M|        while (pos < max_starting_point) {
  ------------------
  |  Branch (181:16): [True: 1.92M, False: 215k]
  ------------------
  182|       |          // Performance note: our ability to compute 'consumed' and
  183|       |          // then shift and recompute is critical. If there is a
  184|       |          // latency of, say, 4 cycles on getting 'consumed', then
  185|       |          // the inner loop might have a total latency of about 6 cycles.
  186|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  187|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  188|       |          // for this section of the code. Hence, there is a limit
  189|       |          // to how much we can further increase this latency before
  190|       |          // it seriously harms performance.
  191|  1.92M|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  192|  1.92M|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  193|  1.92M|          pos += consumed;
  194|  1.92M|          utf8_end_of_code_point_mask >>= consumed;
  195|  1.92M|        }
  196|       |        // At this point there may remain between 0 and 12 bytes in the
  197|       |        // 64-byte block. These bytes will be processed again. So we have an
  198|       |        // 80% efficiency (in the worst case). In practice we expect an
  199|       |        // 85% to 90% efficiency.
  200|   215k|      }
  201|   521k|    }
  202|    726|    if (errors()) {
  ------------------
  |  Branch (202:9): [True: 350, False: 376]
  ------------------
  203|    350|      return 0;
  204|    350|    }
  205|    376|    if (pos < size) {
  ------------------
  |  Branch (205:9): [True: 374, False: 2]
  ------------------
  206|    374|      size_t howmany = scalar::utf8_to_utf16::convert<endian>(
  207|    374|          in + pos, size - pos, utf16_output);
  208|    374|      if (howmany == 0) {
  ------------------
  |  Branch (208:11): [True: 184, False: 190]
  ------------------
  209|    184|        return 0;
  210|    184|      }
  211|    190|      utf16_output += howmany;
  212|    190|    }
  213|    192|    return utf16_output - start;
  214|    376|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoder19convert_with_errorsILNS_10endiannessE0EEENS_6resultEPKcmPDs:
  218|    568|                                                   char16_t *utf16_output) {
  219|    568|    size_t pos = 0;
  220|    568|    char16_t *start{utf16_output};
  221|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  222|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  223|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  224|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  225|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  226|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  227|    568|    size_t leading_byte = 0;
  228|    568|    size_t margin = size;
  229|  4.87k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (229:12): [True: 4.64k, False: 230]
  |  Branch (229:26): [True: 4.30k, False: 338]
  ------------------
  230|  4.30k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  231|  4.30k|    }
  232|       |    // If the input is long enough, then we have that margin-1 is the eight last
  233|       |    // leading byte.
  234|    568|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  235|   415k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (235:12): [True: 414k, False: 490]
  ------------------
  236|   414k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  237|   414k|      if (input.is_ascii()) {
  ------------------
  |  Branch (237:11): [True: 330k, False: 83.7k]
  ------------------
  238|   330k|        input.store_ascii_as_utf16<endian>(utf16_output);
  239|   330k|        utf16_output += 64;
  240|   330k|        pos += 64;
  241|   330k|      } else {
  242|       |        // you might think that a for-loop would work, but under Visual Studio,
  243|       |        // it is not good enough.
  244|  83.7k|        static_assert(
  245|  83.7k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  246|  83.7k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  247|  83.7k|            "We support either two or four chunks per 64-byte block.");
  248|  83.7k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  249|  83.7k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  250|  83.7k|          this->check_utf8_bytes(input.chunks[0], zero);
  251|  83.7k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  252|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  253|       |          this->check_utf8_bytes(input.chunks[0], zero);
  254|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  255|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  256|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  257|       |        }
  258|  83.7k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  259|  83.7k|        if (errors() || (utf8_continuation_mask & 1)) {
  ------------------
  |  Branch (259:13): [True: 78, False: 83.6k]
  |  Branch (259:25): [True: 0, False: 83.6k]
  ------------------
  260|       |          // rewind_and_convert_with_errors will seek a potential error from
  261|       |          // in+pos onward, with the ability to go back up to pos bytes, and
  262|       |          // read size-pos bytes forward.
  263|     78|          result res =
  264|     78|              scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  265|     78|                  pos, in + pos, size - pos, utf16_output);
  266|     78|          res.count += pos;
  267|     78|          return res;
  268|     78|        }
  269|  83.6k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  270|  83.6k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  271|       |        // We process in blocks of up to 12 bytes except possibly
  272|       |        // for fast paths which may process up to 16 bytes. For the
  273|       |        // slow path to work, we should have at least 12 input bytes left.
  274|  83.6k|        size_t max_starting_point = (pos + 64) - 12;
  275|       |        // Next loop is going to run at least five times.
  276|   823k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (276:16): [True: 740k, False: 83.6k]
  ------------------
  277|       |          // Performance note: our ability to compute 'consumed' and
  278|       |          // then shift and recompute is critical. If there is a
  279|       |          // latency of, say, 4 cycles on getting 'consumed', then
  280|       |          // the inner loop might have a total latency of about 6 cycles.
  281|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  282|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  283|       |          // for this section of the code. Hence, there is a limit
  284|       |          // to how much we can further increase this latency before
  285|       |          // it seriously harms performance.
  286|   740k|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  287|   740k|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  288|   740k|          pos += consumed;
  289|   740k|          utf8_end_of_code_point_mask >>= consumed;
  290|   740k|        }
  291|       |        // At this point there may remain between 0 and 12 bytes in the
  292|       |        // 64-byte block. These bytes will be processed again. So we have an
  293|       |        // 80% efficiency (in the worst case). In practice we expect an
  294|       |        // 85% to 90% efficiency.
  295|  83.6k|      }
  296|   414k|    }
  297|    490|    if (errors()) {
  ------------------
  |  Branch (297:9): [True: 0, False: 490]
  ------------------
  298|       |      // rewind_and_convert_with_errors will seek a potential error from in+pos
  299|       |      // onward, with the ability to go back up to pos bytes, and read size-pos
  300|       |      // bytes forward.
  301|      0|      result res =
  302|      0|          scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  303|      0|              pos, in + pos, size - pos, utf16_output);
  304|      0|      res.count += pos;
  305|      0|      return res;
  306|      0|    }
  307|    490|    if (pos < size) {
  ------------------
  |  Branch (307:9): [True: 486, False: 4]
  ------------------
  308|       |      // rewind_and_convert_with_errors will seek a potential error from in+pos
  309|       |      // onward, with the ability to go back up to pos bytes, and read size-pos
  310|       |      // bytes forward.
  311|    486|      result res =
  312|    486|          scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  313|    486|              pos, in + pos, size - pos, utf16_output);
  314|    486|      if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (314:11): [True: 238, False: 248]
  ------------------
  315|    238|        res.count += pos;
  316|    238|        return res;
  317|    248|      } else { // In case of success, we want the number of word written
  318|    248|        utf16_output += res.count;
  319|    248|      }
  320|    486|    }
  321|    252|    return result(error_code::SUCCESS, utf16_output - start);
  322|    490|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1621validating_transcoder19convert_with_errorsILNS_10endiannessE1EEENS_6resultEPKcmPDs:
  218|    758|                                                   char16_t *utf16_output) {
  219|    758|    size_t pos = 0;
  220|    758|    char16_t *start{utf16_output};
  221|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  222|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  223|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  224|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  225|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  226|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  227|    758|    size_t leading_byte = 0;
  228|    758|    size_t margin = size;
  229|  6.74k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (229:12): [True: 6.49k, False: 258]
  |  Branch (229:26): [True: 5.99k, False: 500]
  ------------------
  230|  5.99k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  231|  5.99k|    }
  232|       |    // If the input is long enough, then we have that margin-1 is the eight last
  233|       |    // leading byte.
  234|    758|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  235|   496k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (235:12): [True: 495k, False: 666]
  ------------------
  236|   495k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  237|   495k|      if (input.is_ascii()) {
  ------------------
  |  Branch (237:11): [True: 398k, False: 96.8k]
  ------------------
  238|   398k|        input.store_ascii_as_utf16<endian>(utf16_output);
  239|   398k|        utf16_output += 64;
  240|   398k|        pos += 64;
  241|   398k|      } else {
  242|       |        // you might think that a for-loop would work, but under Visual Studio,
  243|       |        // it is not good enough.
  244|  96.8k|        static_assert(
  245|  96.8k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  246|  96.8k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  247|  96.8k|            "We support either two or four chunks per 64-byte block.");
  248|  96.8k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  249|  96.8k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  250|  96.8k|          this->check_utf8_bytes(input.chunks[0], zero);
  251|  96.8k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  252|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  253|       |          this->check_utf8_bytes(input.chunks[0], zero);
  254|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  255|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  256|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  257|       |        }
  258|  96.8k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  259|  96.8k|        if (errors() || (utf8_continuation_mask & 1)) {
  ------------------
  |  Branch (259:13): [True: 92, False: 96.7k]
  |  Branch (259:25): [True: 0, False: 96.7k]
  ------------------
  260|       |          // rewind_and_convert_with_errors will seek a potential error from
  261|       |          // in+pos onward, with the ability to go back up to pos bytes, and
  262|       |          // read size-pos bytes forward.
  263|     92|          result res =
  264|     92|              scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  265|     92|                  pos, in + pos, size - pos, utf16_output);
  266|     92|          res.count += pos;
  267|     92|          return res;
  268|     92|        }
  269|  96.7k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  270|  96.7k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  271|       |        // We process in blocks of up to 12 bytes except possibly
  272|       |        // for fast paths which may process up to 16 bytes. For the
  273|       |        // slow path to work, we should have at least 12 input bytes left.
  274|  96.7k|        size_t max_starting_point = (pos + 64) - 12;
  275|       |        // Next loop is going to run at least five times.
  276|   940k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (276:16): [True: 843k, False: 96.7k]
  ------------------
  277|       |          // Performance note: our ability to compute 'consumed' and
  278|       |          // then shift and recompute is critical. If there is a
  279|       |          // latency of, say, 4 cycles on getting 'consumed', then
  280|       |          // the inner loop might have a total latency of about 6 cycles.
  281|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  282|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  283|       |          // for this section of the code. Hence, there is a limit
  284|       |          // to how much we can further increase this latency before
  285|       |          // it seriously harms performance.
  286|   843k|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  287|   843k|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  288|   843k|          pos += consumed;
  289|   843k|          utf8_end_of_code_point_mask >>= consumed;
  290|   843k|        }
  291|       |        // At this point there may remain between 0 and 12 bytes in the
  292|       |        // 64-byte block. These bytes will be processed again. So we have an
  293|       |        // 80% efficiency (in the worst case). In practice we expect an
  294|       |        // 85% to 90% efficiency.
  295|  96.7k|      }
  296|   495k|    }
  297|    666|    if (errors()) {
  ------------------
  |  Branch (297:9): [True: 0, False: 666]
  ------------------
  298|       |      // rewind_and_convert_with_errors will seek a potential error from in+pos
  299|       |      // onward, with the ability to go back up to pos bytes, and read size-pos
  300|       |      // bytes forward.
  301|      0|      result res =
  302|      0|          scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  303|      0|              pos, in + pos, size - pos, utf16_output);
  304|      0|      res.count += pos;
  305|      0|      return res;
  306|      0|    }
  307|    666|    if (pos < size) {
  ------------------
  |  Branch (307:9): [True: 662, False: 4]
  ------------------
  308|       |      // rewind_and_convert_with_errors will seek a potential error from in+pos
  309|       |      // onward, with the ability to go back up to pos bytes, and read size-pos
  310|       |      // bytes forward.
  311|    662|      result res =
  312|    662|          scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  313|    662|              pos, in + pos, size - pos, utf16_output);
  314|    662|      if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (314:11): [True: 328, False: 334]
  ------------------
  315|    328|        res.count += pos;
  316|    328|        return res;
  317|    334|      } else { // In case of success, we want the number of word written
  318|    334|        utf16_output += res.count;
  319|    334|      }
  320|    662|    }
  321|    338|    return result(error_code::SUCCESS, utf16_output - start);
  322|    666|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoder7convertILNS_10endiannessE0EEEmPKcmPDs:
  130|    754|                                       char16_t *utf16_output) {
  131|    754|    size_t pos = 0;
  132|    754|    char16_t *start{utf16_output};
  133|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  134|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  135|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  136|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  137|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  138|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  139|    754|    size_t leading_byte = 0;
  140|    754|    size_t margin = size;
  141|  7.21k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (141:12): [True: 6.96k, False: 246]
  |  Branch (141:26): [True: 6.45k, False: 508]
  ------------------
  142|  6.45k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  143|  6.45k|    }
  144|       |    // If the input is long enough, then we have that margin-1 is the eight last
  145|       |    // leading byte.
  146|    754|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  147|   564k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (147:12): [True: 563k, False: 732]
  ------------------
  148|   563k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  149|   563k|      if (input.is_ascii()) {
  ------------------
  |  Branch (149:11): [True: 466k, False: 97.6k]
  ------------------
  150|   466k|        input.store_ascii_as_utf16<endian>(utf16_output);
  151|   466k|        utf16_output += 64;
  152|   466k|        pos += 64;
  153|   466k|      } else {
  154|       |        // you might think that a for-loop would work, but under Visual Studio,
  155|       |        // it is not good enough.
  156|  97.6k|        static_assert(
  157|  97.6k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  158|  97.6k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  159|  97.6k|            "We support either two or four chunks per 64-byte block.");
  160|  97.6k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  161|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  162|       |          this->check_utf8_bytes(input.chunks[0], zero);
  163|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  164|  97.6k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  165|  97.6k|          this->check_utf8_bytes(input.chunks[0], zero);
  166|  97.6k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  167|  97.6k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  168|  97.6k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  169|  97.6k|        }
  170|  97.6k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  171|  97.6k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (171:13): [True: 22, False: 97.5k]
  ------------------
  172|     22|          return 0; // error
  173|     22|        }
  174|  97.5k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  175|  97.5k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  176|       |        // We process in blocks of up to 12 bytes except possibly
  177|       |        // for fast paths which may process up to 16 bytes. For the
  178|       |        // slow path to work, we should have at least 12 input bytes left.
  179|  97.5k|        size_t max_starting_point = (pos + 64) - 12;
  180|       |        // Next loop is going to run at least five times.
  181|   948k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (181:16): [True: 850k, False: 97.5k]
  ------------------
  182|       |          // Performance note: our ability to compute 'consumed' and
  183|       |          // then shift and recompute is critical. If there is a
  184|       |          // latency of, say, 4 cycles on getting 'consumed', then
  185|       |          // the inner loop might have a total latency of about 6 cycles.
  186|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  187|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  188|       |          // for this section of the code. Hence, there is a limit
  189|       |          // to how much we can further increase this latency before
  190|       |          // it seriously harms performance.
  191|   850k|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  192|   850k|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  193|   850k|          pos += consumed;
  194|   850k|          utf8_end_of_code_point_mask >>= consumed;
  195|   850k|        }
  196|       |        // At this point there may remain between 0 and 12 bytes in the
  197|       |        // 64-byte block. These bytes will be processed again. So we have an
  198|       |        // 80% efficiency (in the worst case). In practice we expect an
  199|       |        // 85% to 90% efficiency.
  200|  97.5k|      }
  201|   563k|    }
  202|    732|    if (errors()) {
  ------------------
  |  Branch (202:9): [True: 292, False: 440]
  ------------------
  203|    292|      return 0;
  204|    292|    }
  205|    440|    if (pos < size) {
  ------------------
  |  Branch (205:9): [True: 436, False: 4]
  ------------------
  206|    436|      size_t howmany = scalar::utf8_to_utf16::convert<endian>(
  207|    436|          in + pos, size - pos, utf16_output);
  208|    436|      if (howmany == 0) {
  ------------------
  |  Branch (208:11): [True: 204, False: 232]
  ------------------
  209|    204|        return 0;
  210|    204|      }
  211|    232|      utf16_output += howmany;
  212|    232|    }
  213|    236|    return utf16_output - start;
  214|    440|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoder16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  119|  1.97M|                                              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.97M|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  124|  1.97M|    simd8<uint8_t> sc = check_special_cases(input, prev1);
  125|  1.97M|    this->error |= check_multibyte_lengths(input, prev_input, sc);
  126|  1.97M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1619check_special_casesENS1_4simd5simd8IhEES5_:
    8|  1.97M|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.97M|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   16|       |                                               // 11______ 11______
   17|  1.97M|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   18|  1.97M|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   19|  1.97M|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   20|  1.97M|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   21|  1.97M|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   22|  1.97M|  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.97M|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   31|       |  // 11110101 1000____
   32|       |  // 1111011_ 1000____
   33|       |  // 11111___ 1000____
   34|  1.97M|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   35|       |
   36|  1.97M|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   37|       |      // 0_______ ________ <ASCII in byte 1>
   38|  1.97M|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   39|  1.97M|      TOO_LONG,
   40|       |      // 10______ ________ <continuation in byte 1>
   41|  1.97M|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   42|       |      // 1100____ ________ <two byte lead in byte 1>
   43|  1.97M|      TOO_SHORT | OVERLONG_2,
   44|       |      // 1101____ ________ <two byte lead in byte 1>
   45|  1.97M|      TOO_SHORT,
   46|       |      // 1110____ ________ <three byte lead in byte 1>
   47|  1.97M|      TOO_SHORT | OVERLONG_3 | SURROGATE,
   48|       |      // 1111____ ________ <four+ byte lead in byte 1>
   49|  1.97M|      TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4);
   50|  1.97M|  constexpr const uint8_t CARRY =
   51|  1.97M|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   52|  1.97M|  const simd8<uint8_t> byte_1_low =
   53|  1.97M|      (prev1 & 0x0F)
   54|  1.97M|          .lookup_16<uint8_t>(
   55|       |              // ____0000 ________
   56|  1.97M|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   57|       |              // ____0001 ________
   58|  1.97M|              CARRY | OVERLONG_2,
   59|       |              // ____001_ ________
   60|  1.97M|              CARRY, CARRY,
   61|       |
   62|       |              // ____0100 ________
   63|  1.97M|              CARRY | TOO_LARGE,
   64|       |              // ____0101 ________
   65|  1.97M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   66|       |              // ____011_ ________
   67|  1.97M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   68|  1.97M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   69|       |
   70|       |              // ____1___ ________
   71|  1.97M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   72|  1.97M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   73|  1.97M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   74|  1.97M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   75|  1.97M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   76|       |              // ____1101 ________
   77|  1.97M|              CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
   78|  1.97M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   79|  1.97M|              CARRY | TOO_LARGE | TOO_LARGE_1000);
   80|  1.97M|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   81|       |      // ________ 0_______ <ASCII in byte 2>
   82|  1.97M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   83|  1.97M|      TOO_SHORT, TOO_SHORT,
   84|       |
   85|       |      // ________ 1000____
   86|  1.97M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   87|  1.97M|          OVERLONG_4,
   88|       |      // ________ 1001____
   89|  1.97M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   90|       |      // ________ 101_____
   91|  1.97M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   92|  1.97M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   93|       |
   94|       |      // ________ 11______
   95|  1.97M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   96|  1.97M|  return (byte_1_high & byte_1_low & byte_2_high);
   97|  1.97M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1623check_multibyte_lengthsENS1_4simd5simd8IhEES5_S5_:
  101|  1.97M|                        const simd8<uint8_t> sc) {
  102|  1.97M|  simd8<uint8_t> prev2 = input.prev<2>(prev_input);
  103|  1.97M|  simd8<uint8_t> prev3 = input.prev<3>(prev_input);
  104|  1.97M|  simd8<uint8_t> must23 =
  105|  1.97M|      simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
  106|  1.97M|  simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
  107|  1.97M|  return must23_80 ^ sc;
  108|  1.97M|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoder6errorsEv:
  324|   183k|  simdutf_really_inline bool errors() const {
  325|   183k|    return this->error.any_bits_set_anywhere();
  326|   183k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoder7convertILNS_10endiannessE1EEEmPKcmPDs:
  130|    758|                                       char16_t *utf16_output) {
  131|    758|    size_t pos = 0;
  132|    758|    char16_t *start{utf16_output};
  133|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  134|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  135|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  136|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  137|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  138|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  139|    758|    size_t leading_byte = 0;
  140|    758|    size_t margin = size;
  141|  7.45k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (141:12): [True: 7.22k, False: 226]
  |  Branch (141:26): [True: 6.69k, False: 532]
  ------------------
  142|  6.69k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  143|  6.69k|    }
  144|       |    // If the input is long enough, then we have that margin-1 is the eight last
  145|       |    // leading byte.
  146|    758|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  147|   521k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (147:12): [True: 521k, False: 726]
  ------------------
  148|   521k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  149|   521k|      if (input.is_ascii()) {
  ------------------
  |  Branch (149:11): [True: 305k, False: 215k]
  ------------------
  150|   305k|        input.store_ascii_as_utf16<endian>(utf16_output);
  151|   305k|        utf16_output += 64;
  152|   305k|        pos += 64;
  153|   305k|      } else {
  154|       |        // you might think that a for-loop would work, but under Visual Studio,
  155|       |        // it is not good enough.
  156|   215k|        static_assert(
  157|   215k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  158|   215k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  159|   215k|            "We support either two or four chunks per 64-byte block.");
  160|   215k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  161|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  162|       |          this->check_utf8_bytes(input.chunks[0], zero);
  163|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  164|   215k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  165|   215k|          this->check_utf8_bytes(input.chunks[0], zero);
  166|   215k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  167|   215k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  168|   215k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  169|   215k|        }
  170|   215k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  171|   215k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (171:13): [True: 32, False: 215k]
  ------------------
  172|     32|          return 0; // error
  173|     32|        }
  174|   215k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  175|   215k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  176|       |        // We process in blocks of up to 12 bytes except possibly
  177|       |        // for fast paths which may process up to 16 bytes. For the
  178|       |        // slow path to work, we should have at least 12 input bytes left.
  179|   215k|        size_t max_starting_point = (pos + 64) - 12;
  180|       |        // Next loop is going to run at least five times.
  181|  2.14M|        while (pos < max_starting_point) {
  ------------------
  |  Branch (181:16): [True: 1.92M, False: 215k]
  ------------------
  182|       |          // Performance note: our ability to compute 'consumed' and
  183|       |          // then shift and recompute is critical. If there is a
  184|       |          // latency of, say, 4 cycles on getting 'consumed', then
  185|       |          // the inner loop might have a total latency of about 6 cycles.
  186|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  187|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  188|       |          // for this section of the code. Hence, there is a limit
  189|       |          // to how much we can further increase this latency before
  190|       |          // it seriously harms performance.
  191|  1.92M|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  192|  1.92M|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  193|  1.92M|          pos += consumed;
  194|  1.92M|          utf8_end_of_code_point_mask >>= consumed;
  195|  1.92M|        }
  196|       |        // At this point there may remain between 0 and 12 bytes in the
  197|       |        // 64-byte block. These bytes will be processed again. So we have an
  198|       |        // 80% efficiency (in the worst case). In practice we expect an
  199|       |        // 85% to 90% efficiency.
  200|   215k|      }
  201|   521k|    }
  202|    726|    if (errors()) {
  ------------------
  |  Branch (202:9): [True: 350, False: 376]
  ------------------
  203|    350|      return 0;
  204|    350|    }
  205|    376|    if (pos < size) {
  ------------------
  |  Branch (205:9): [True: 374, False: 2]
  ------------------
  206|    374|      size_t howmany = scalar::utf8_to_utf16::convert<endian>(
  207|    374|          in + pos, size - pos, utf16_output);
  208|    374|      if (howmany == 0) {
  ------------------
  |  Branch (208:11): [True: 184, False: 190]
  ------------------
  209|    184|        return 0;
  210|    184|      }
  211|    190|      utf16_output += howmany;
  212|    190|    }
  213|    192|    return utf16_output - start;
  214|    376|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoder19convert_with_errorsILNS_10endiannessE0EEENS_6resultEPKcmPDs:
  218|    568|                                                   char16_t *utf16_output) {
  219|    568|    size_t pos = 0;
  220|    568|    char16_t *start{utf16_output};
  221|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  222|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  223|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  224|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  225|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  226|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  227|    568|    size_t leading_byte = 0;
  228|    568|    size_t margin = size;
  229|  4.87k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (229:12): [True: 4.64k, False: 230]
  |  Branch (229:26): [True: 4.30k, False: 338]
  ------------------
  230|  4.30k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  231|  4.30k|    }
  232|       |    // If the input is long enough, then we have that margin-1 is the eight last
  233|       |    // leading byte.
  234|    568|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  235|   415k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (235:12): [True: 414k, False: 490]
  ------------------
  236|   414k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  237|   414k|      if (input.is_ascii()) {
  ------------------
  |  Branch (237:11): [True: 330k, False: 83.7k]
  ------------------
  238|   330k|        input.store_ascii_as_utf16<endian>(utf16_output);
  239|   330k|        utf16_output += 64;
  240|   330k|        pos += 64;
  241|   330k|      } else {
  242|       |        // you might think that a for-loop would work, but under Visual Studio,
  243|       |        // it is not good enough.
  244|  83.7k|        static_assert(
  245|  83.7k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  246|  83.7k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  247|  83.7k|            "We support either two or four chunks per 64-byte block.");
  248|  83.7k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  249|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  250|       |          this->check_utf8_bytes(input.chunks[0], zero);
  251|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  252|  83.7k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  253|  83.7k|          this->check_utf8_bytes(input.chunks[0], zero);
  254|  83.7k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  255|  83.7k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  256|  83.7k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  257|  83.7k|        }
  258|  83.7k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  259|  83.7k|        if (errors() || (utf8_continuation_mask & 1)) {
  ------------------
  |  Branch (259:13): [True: 78, False: 83.6k]
  |  Branch (259:25): [True: 0, False: 83.6k]
  ------------------
  260|       |          // rewind_and_convert_with_errors will seek a potential error from
  261|       |          // in+pos onward, with the ability to go back up to pos bytes, and
  262|       |          // read size-pos bytes forward.
  263|     78|          result res =
  264|     78|              scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  265|     78|                  pos, in + pos, size - pos, utf16_output);
  266|     78|          res.count += pos;
  267|     78|          return res;
  268|     78|        }
  269|  83.6k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  270|  83.6k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  271|       |        // We process in blocks of up to 12 bytes except possibly
  272|       |        // for fast paths which may process up to 16 bytes. For the
  273|       |        // slow path to work, we should have at least 12 input bytes left.
  274|  83.6k|        size_t max_starting_point = (pos + 64) - 12;
  275|       |        // Next loop is going to run at least five times.
  276|   823k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (276:16): [True: 740k, False: 83.6k]
  ------------------
  277|       |          // Performance note: our ability to compute 'consumed' and
  278|       |          // then shift and recompute is critical. If there is a
  279|       |          // latency of, say, 4 cycles on getting 'consumed', then
  280|       |          // the inner loop might have a total latency of about 6 cycles.
  281|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  282|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  283|       |          // for this section of the code. Hence, there is a limit
  284|       |          // to how much we can further increase this latency before
  285|       |          // it seriously harms performance.
  286|   740k|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  287|   740k|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  288|   740k|          pos += consumed;
  289|   740k|          utf8_end_of_code_point_mask >>= consumed;
  290|   740k|        }
  291|       |        // At this point there may remain between 0 and 12 bytes in the
  292|       |        // 64-byte block. These bytes will be processed again. So we have an
  293|       |        // 80% efficiency (in the worst case). In practice we expect an
  294|       |        // 85% to 90% efficiency.
  295|  83.6k|      }
  296|   414k|    }
  297|    490|    if (errors()) {
  ------------------
  |  Branch (297:9): [True: 0, False: 490]
  ------------------
  298|       |      // rewind_and_convert_with_errors will seek a potential error from in+pos
  299|       |      // onward, with the ability to go back up to pos bytes, and read size-pos
  300|       |      // bytes forward.
  301|      0|      result res =
  302|      0|          scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  303|      0|              pos, in + pos, size - pos, utf16_output);
  304|      0|      res.count += pos;
  305|      0|      return res;
  306|      0|    }
  307|    490|    if (pos < size) {
  ------------------
  |  Branch (307:9): [True: 486, False: 4]
  ------------------
  308|       |      // rewind_and_convert_with_errors will seek a potential error from in+pos
  309|       |      // onward, with the ability to go back up to pos bytes, and read size-pos
  310|       |      // bytes forward.
  311|    486|      result res =
  312|    486|          scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  313|    486|              pos, in + pos, size - pos, utf16_output);
  314|    486|      if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (314:11): [True: 238, False: 248]
  ------------------
  315|    238|        res.count += pos;
  316|    238|        return res;
  317|    248|      } else { // In case of success, we want the number of word written
  318|    248|        utf16_output += res.count;
  319|    248|      }
  320|    486|    }
  321|    252|    return result(error_code::SUCCESS, utf16_output - start);
  322|    490|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1621validating_transcoder19convert_with_errorsILNS_10endiannessE1EEENS_6resultEPKcmPDs:
  218|    758|                                                   char16_t *utf16_output) {
  219|    758|    size_t pos = 0;
  220|    758|    char16_t *start{utf16_output};
  221|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  222|       |    // of 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the
  223|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  224|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  225|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  226|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  227|    758|    size_t leading_byte = 0;
  228|    758|    size_t margin = size;
  229|  6.74k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (229:12): [True: 6.49k, False: 258]
  |  Branch (229:26): [True: 5.99k, False: 500]
  ------------------
  230|  5.99k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  231|  5.99k|    }
  232|       |    // If the input is long enough, then we have that margin-1 is the eight last
  233|       |    // leading byte.
  234|    758|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  235|   496k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (235:12): [True: 495k, False: 666]
  ------------------
  236|   495k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  237|   495k|      if (input.is_ascii()) {
  ------------------
  |  Branch (237:11): [True: 398k, False: 96.8k]
  ------------------
  238|   398k|        input.store_ascii_as_utf16<endian>(utf16_output);
  239|   398k|        utf16_output += 64;
  240|   398k|        pos += 64;
  241|   398k|      } else {
  242|       |        // you might think that a for-loop would work, but under Visual Studio,
  243|       |        // it is not good enough.
  244|  96.8k|        static_assert(
  245|  96.8k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  246|  96.8k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  247|  96.8k|            "We support either two or four chunks per 64-byte block.");
  248|  96.8k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  249|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  250|       |          this->check_utf8_bytes(input.chunks[0], zero);
  251|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  252|  96.8k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  253|  96.8k|          this->check_utf8_bytes(input.chunks[0], zero);
  254|  96.8k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  255|  96.8k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  256|  96.8k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  257|  96.8k|        }
  258|  96.8k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  259|  96.8k|        if (errors() || (utf8_continuation_mask & 1)) {
  ------------------
  |  Branch (259:13): [True: 92, False: 96.7k]
  |  Branch (259:25): [True: 0, False: 96.7k]
  ------------------
  260|       |          // rewind_and_convert_with_errors will seek a potential error from
  261|       |          // in+pos onward, with the ability to go back up to pos bytes, and
  262|       |          // read size-pos bytes forward.
  263|     92|          result res =
  264|     92|              scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  265|     92|                  pos, in + pos, size - pos, utf16_output);
  266|     92|          res.count += pos;
  267|     92|          return res;
  268|     92|        }
  269|  96.7k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  270|  96.7k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  271|       |        // We process in blocks of up to 12 bytes except possibly
  272|       |        // for fast paths which may process up to 16 bytes. For the
  273|       |        // slow path to work, we should have at least 12 input bytes left.
  274|  96.7k|        size_t max_starting_point = (pos + 64) - 12;
  275|       |        // Next loop is going to run at least five times.
  276|   940k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (276:16): [True: 843k, False: 96.7k]
  ------------------
  277|       |          // Performance note: our ability to compute 'consumed' and
  278|       |          // then shift and recompute is critical. If there is a
  279|       |          // latency of, say, 4 cycles on getting 'consumed', then
  280|       |          // the inner loop might have a total latency of about 6 cycles.
  281|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  282|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  283|       |          // for this section of the code. Hence, there is a limit
  284|       |          // to how much we can further increase this latency before
  285|       |          // it seriously harms performance.
  286|   843k|          size_t consumed = convert_masked_utf8_to_utf16<endian>(
  287|   843k|              in + pos, utf8_end_of_code_point_mask, utf16_output);
  288|   843k|          pos += consumed;
  289|   843k|          utf8_end_of_code_point_mask >>= consumed;
  290|   843k|        }
  291|       |        // At this point there may remain between 0 and 12 bytes in the
  292|       |        // 64-byte block. These bytes will be processed again. So we have an
  293|       |        // 80% efficiency (in the worst case). In practice we expect an
  294|       |        // 85% to 90% efficiency.
  295|  96.7k|      }
  296|   495k|    }
  297|    666|    if (errors()) {
  ------------------
  |  Branch (297:9): [True: 0, False: 666]
  ------------------
  298|       |      // rewind_and_convert_with_errors will seek a potential error from in+pos
  299|       |      // onward, with the ability to go back up to pos bytes, and read size-pos
  300|       |      // bytes forward.
  301|      0|      result res =
  302|      0|          scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  303|      0|              pos, in + pos, size - pos, utf16_output);
  304|      0|      res.count += pos;
  305|      0|      return res;
  306|      0|    }
  307|    666|    if (pos < size) {
  ------------------
  |  Branch (307:9): [True: 662, False: 4]
  ------------------
  308|       |      // rewind_and_convert_with_errors will seek a potential error from in+pos
  309|       |      // onward, with the ability to go back up to pos bytes, and read size-pos
  310|       |      // bytes forward.
  311|    662|      result res =
  312|    662|          scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(
  313|    662|              pos, in + pos, size - pos, utf16_output);
  314|    662|      if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (314:11): [True: 328, False: 334]
  ------------------
  315|    328|        res.count += pos;
  316|    328|        return res;
  317|    334|      } else { // In case of success, we want the number of word written
  318|    334|        utf16_output += res.count;
  319|    334|      }
  320|    662|    }
  321|    338|    return result(error_code::SUCCESS, utf16_output - start);
  322|    666|  }

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1613convert_validILNS_10endiannessE0EEEmPKcmPDs:
   10|    438|                                         char16_t *utf16_output) noexcept {
   11|       |  // The implementation is not specific to haswell and should be moved to the
   12|       |  // generic directory.
   13|    438|  size_t pos = 0;
   14|    438|  char16_t *start{utf16_output};
   15|    438|  const size_t safety_margin = 16; // to avoid overruns!
   16|   401k|  while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (16:10): [True: 401k, False: 438]
  ------------------
   17|       |    // this loop could be unrolled further. For example, we could process the
   18|       |    // mask far more than 64 bytes.
   19|   401k|    simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
   20|   401k|    if (in.is_ascii()) {
  ------------------
  |  Branch (20:9): [True: 238k, False: 162k]
  ------------------
   21|   238k|      in.store_ascii_as_utf16<endian>(utf16_output);
   22|   238k|      utf16_output += 64;
   23|   238k|      pos += 64;
   24|   238k|    } 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|   162k|      uint64_t utf8_continuation_mask = in.lt(-65 + 1);
   29|       |      // -65 is 0b10111111 in two-complement's, so largest possible continuation
   30|       |      // byte
   31|   162k|      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|   162k|      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|   162k|      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.60M|      while (pos < max_starting_point) {
  ------------------
  |  Branch (41:14): [True: 1.44M, False: 162k]
  ------------------
   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.44M|        size_t consumed = convert_masked_utf8_to_utf16<endian>(
   56|  1.44M|            input + pos, utf8_end_of_code_point_mask, utf16_output);
   57|  1.44M|        pos += consumed;
   58|  1.44M|        utf8_end_of_code_point_mask >>= consumed;
   59|  1.44M|      }
   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|   162k|    }
   65|   401k|  }
   66|    438|  utf16_output += scalar::utf8_to_utf16::convert_valid<endian>(
   67|    438|      input + pos, size - pos, utf16_output);
   68|    438|  return utf16_output - start;
   69|    438|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf1613convert_validILNS_10endiannessE1EEEmPKcmPDs:
   10|    456|                                         char16_t *utf16_output) noexcept {
   11|       |  // The implementation is not specific to haswell and should be moved to the
   12|       |  // generic directory.
   13|    456|  size_t pos = 0;
   14|    456|  char16_t *start{utf16_output};
   15|    456|  const size_t safety_margin = 16; // to avoid overruns!
   16|   490k|  while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (16:10): [True: 490k, False: 456]
  ------------------
   17|       |    // this loop could be unrolled further. For example, we could process the
   18|       |    // mask far more than 64 bytes.
   19|   490k|    simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
   20|   490k|    if (in.is_ascii()) {
  ------------------
  |  Branch (20:9): [True: 425k, False: 64.9k]
  ------------------
   21|   425k|      in.store_ascii_as_utf16<endian>(utf16_output);
   22|   425k|      utf16_output += 64;
   23|   425k|      pos += 64;
   24|   425k|    } 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|  64.9k|      uint64_t utf8_continuation_mask = in.lt(-65 + 1);
   29|       |      // -65 is 0b10111111 in two-complement's, so largest possible continuation
   30|       |      // byte
   31|  64.9k|      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|  64.9k|      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|  64.9k|      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|   608k|      while (pos < max_starting_point) {
  ------------------
  |  Branch (41:14): [True: 543k, False: 64.9k]
  ------------------
   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|   543k|        size_t consumed = convert_masked_utf8_to_utf16<endian>(
   56|   543k|            input + pos, utf8_end_of_code_point_mask, utf16_output);
   57|   543k|        pos += consumed;
   58|   543k|        utf8_end_of_code_point_mask >>= consumed;
   59|   543k|      }
   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|  64.9k|    }
   65|   490k|  }
   66|    456|  utf16_output += scalar::utf8_to_utf16::convert_valid<endian>(
   67|    456|      input + pos, size - pos, utf16_output);
   68|    456|  return utf16_output - start;
   69|    456|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1613convert_validILNS_10endiannessE0EEEmPKcmPDs:
   10|    438|                                         char16_t *utf16_output) noexcept {
   11|       |  // The implementation is not specific to haswell and should be moved to the
   12|       |  // generic directory.
   13|    438|  size_t pos = 0;
   14|    438|  char16_t *start{utf16_output};
   15|    438|  const size_t safety_margin = 16; // to avoid overruns!
   16|   401k|  while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (16:10): [True: 401k, False: 438]
  ------------------
   17|       |    // this loop could be unrolled further. For example, we could process the
   18|       |    // mask far more than 64 bytes.
   19|   401k|    simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
   20|   401k|    if (in.is_ascii()) {
  ------------------
  |  Branch (20:9): [True: 238k, False: 162k]
  ------------------
   21|   238k|      in.store_ascii_as_utf16<endian>(utf16_output);
   22|   238k|      utf16_output += 64;
   23|   238k|      pos += 64;
   24|   238k|    } 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|   162k|      uint64_t utf8_continuation_mask = in.lt(-65 + 1);
   29|       |      // -65 is 0b10111111 in two-complement's, so largest possible continuation
   30|       |      // byte
   31|   162k|      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|   162k|      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|   162k|      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.60M|      while (pos < max_starting_point) {
  ------------------
  |  Branch (41:14): [True: 1.44M, False: 162k]
  ------------------
   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.44M|        size_t consumed = convert_masked_utf8_to_utf16<endian>(
   56|  1.44M|            input + pos, utf8_end_of_code_point_mask, utf16_output);
   57|  1.44M|        pos += consumed;
   58|  1.44M|        utf8_end_of_code_point_mask >>= consumed;
   59|  1.44M|      }
   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|   162k|    }
   65|   401k|  }
   66|    438|  utf16_output += scalar::utf8_to_utf16::convert_valid<endian>(
   67|    438|      input + pos, size - pos, utf16_output);
   68|    438|  return utf16_output - start;
   69|    438|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf1613convert_validILNS_10endiannessE1EEEmPKcmPDs:
   10|    456|                                         char16_t *utf16_output) noexcept {
   11|       |  // The implementation is not specific to haswell and should be moved to the
   12|       |  // generic directory.
   13|    456|  size_t pos = 0;
   14|    456|  char16_t *start{utf16_output};
   15|    456|  const size_t safety_margin = 16; // to avoid overruns!
   16|   490k|  while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (16:10): [True: 490k, False: 456]
  ------------------
   17|       |    // this loop could be unrolled further. For example, we could process the
   18|       |    // mask far more than 64 bytes.
   19|   490k|    simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
   20|   490k|    if (in.is_ascii()) {
  ------------------
  |  Branch (20:9): [True: 425k, False: 64.9k]
  ------------------
   21|   425k|      in.store_ascii_as_utf16<endian>(utf16_output);
   22|   425k|      utf16_output += 64;
   23|   425k|      pos += 64;
   24|   425k|    } 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|  64.9k|      uint64_t utf8_continuation_mask = in.lt(-65 + 1);
   29|       |      // -65 is 0b10111111 in two-complement's, so largest possible continuation
   30|       |      // byte
   31|  64.9k|      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|  64.9k|      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|  64.9k|      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|   608k|      while (pos < max_starting_point) {
  ------------------
  |  Branch (41:14): [True: 543k, False: 64.9k]
  ------------------
   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|   543k|        size_t consumed = convert_masked_utf8_to_utf16<endian>(
   56|   543k|            input + pos, utf8_end_of_code_point_mask, utf16_output);
   57|   543k|        pos += consumed;
   58|   543k|        utf8_end_of_code_point_mask >>= consumed;
   59|   543k|      }
   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|  64.9k|    }
   65|   490k|  }
   66|    456|  utf16_output += scalar::utf8_to_utf16::convert_valid<endian>(
   67|    456|      input + pos, size - pos, utf16_output);
   68|    456|  return utf16_output - start;
   69|    456|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3221validating_transcoderC2Ev:
  114|  1.52k|  validating_transcoder() : error(uint8_t(0)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3221validating_transcoder7convertEPKcmPDi:
  129|    758|                                       char32_t *utf32_output) {
  130|    758|    size_t pos = 0;
  131|    758|    char32_t *start{utf32_output};
  132|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  133|       |    // of 8 words when calling convert_masked_utf8_to_utf32. If you skip the
  134|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  135|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  136|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  137|       |    // back from the end counting 16 leading bytes, to give us a good margin.
  138|    758|    size_t leading_byte = 0;
  139|    758|    size_t margin = size;
  140|  7.08k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (140:12): [True: 6.85k, False: 232]
  |  Branch (140:26): [True: 6.32k, False: 526]
  ------------------
  141|  6.32k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  142|  6.32k|    }
  143|       |    // If the input is long enough, then we have that margin-1 is the fourth
  144|       |    // last leading byte.
  145|    758|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  146|   719k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (146:12): [True: 719k, False: 736]
  ------------------
  147|   719k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  148|   719k|      if (input.is_ascii()) {
  ------------------
  |  Branch (148:11): [True: 463k, False: 256k]
  ------------------
  149|   463k|        input.store_ascii_as_utf32(utf32_output);
  150|   463k|        utf32_output += 64;
  151|   463k|        pos += 64;
  152|   463k|      } else {
  153|       |        // you might think that a for-loop would work, but under Visual Studio,
  154|       |        // it is not good enough.
  155|   256k|        static_assert(
  156|   256k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  157|   256k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  158|   256k|            "We support either two or four chunks per 64-byte block.");
  159|   256k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  160|   256k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  161|   256k|          this->check_utf8_bytes(input.chunks[0], zero);
  162|   256k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  163|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  164|       |          this->check_utf8_bytes(input.chunks[0], zero);
  165|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  166|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  167|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  168|       |        }
  169|   256k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  170|   256k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (170:13): [True: 22, False: 256k]
  ------------------
  171|     22|          return 0; // we have an error
  172|     22|        }
  173|   256k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  174|   256k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  175|       |        // We process in blocks of up to 12 bytes except possibly
  176|       |        // for fast paths which may process up to 16 bytes. For the
  177|       |        // slow path to work, we should have at least 12 input bytes left.
  178|   256k|        size_t max_starting_point = (pos + 64) - 12;
  179|       |        // Next loop is going to run at least five times.
  180|  2.54M|        while (pos < max_starting_point) {
  ------------------
  |  Branch (180:16): [True: 2.28M, False: 256k]
  ------------------
  181|       |          // Performance note: our ability to compute 'consumed' and
  182|       |          // then shift and recompute is critical. If there is a
  183|       |          // latency of, say, 4 cycles on getting 'consumed', then
  184|       |          // the inner loop might have a total latency of about 6 cycles.
  185|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  186|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  187|       |          // for this section of the code. Hence, there is a limit
  188|       |          // to how much we can further increase this latency before
  189|       |          // it seriously harms performance.
  190|  2.28M|          size_t consumed = convert_masked_utf8_to_utf32(
  191|  2.28M|              in + pos, utf8_end_of_code_point_mask, utf32_output);
  192|  2.28M|          pos += consumed;
  193|  2.28M|          utf8_end_of_code_point_mask >>= consumed;
  194|  2.28M|        }
  195|       |        // At this point there may remain between 0 and 12 bytes in the
  196|       |        // 64-byte block. These bytes will be processed again. So we have an
  197|       |        // 80% efficiency (in the worst case). In practice we expect an
  198|       |        // 85% to 90% efficiency.
  199|   256k|      }
  200|   719k|    }
  201|    736|    if (errors()) {
  ------------------
  |  Branch (201:9): [True: 274, False: 462]
  ------------------
  202|    274|      return 0;
  203|    274|    }
  204|    462|    if (pos < size) {
  ------------------
  |  Branch (204:9): [True: 458, False: 4]
  ------------------
  205|    458|      size_t howmany =
  206|    458|          scalar::utf8_to_utf32::convert(in + pos, size - pos, utf32_output);
  207|    458|      if (howmany == 0) {
  ------------------
  |  Branch (207:11): [True: 180, False: 278]
  ------------------
  208|    180|        return 0;
  209|    180|      }
  210|    278|      utf32_output += howmany;
  211|    278|    }
  212|    282|    return utf32_output - start;
  213|    462|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3221validating_transcoder16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  119|   712k|                                              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|   712k|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  124|   712k|    simd8<uint8_t> sc = check_special_cases(input, prev1);
  125|   712k|    this->error |= check_multibyte_lengths(input, prev_input, sc);
  126|   712k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3219check_special_casesENS1_4simd5simd8IhEES5_:
    8|   712k|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|   712k|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   16|       |                                               // 11______ 11______
   17|   712k|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   18|   712k|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   19|   712k|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   20|   712k|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   21|   712k|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   22|   712k|  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|   712k|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   31|       |  // 11110101 1000____
   32|       |  // 1111011_ 1000____
   33|       |  // 11111___ 1000____
   34|   712k|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   35|       |
   36|   712k|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   37|       |      // 0_______ ________ <ASCII in byte 1>
   38|   712k|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   39|   712k|      TOO_LONG,
   40|       |      // 10______ ________ <continuation in byte 1>
   41|   712k|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   42|       |      // 1100____ ________ <two byte lead in byte 1>
   43|   712k|      TOO_SHORT | OVERLONG_2,
   44|       |      // 1101____ ________ <two byte lead in byte 1>
   45|   712k|      TOO_SHORT,
   46|       |      // 1110____ ________ <three byte lead in byte 1>
   47|   712k|      TOO_SHORT | OVERLONG_3 | SURROGATE,
   48|       |      // 1111____ ________ <four+ byte lead in byte 1>
   49|   712k|      TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4);
   50|   712k|  constexpr const uint8_t CARRY =
   51|   712k|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   52|   712k|  const simd8<uint8_t> byte_1_low =
   53|   712k|      (prev1 & 0x0F)
   54|   712k|          .lookup_16<uint8_t>(
   55|       |              // ____0000 ________
   56|   712k|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   57|       |              // ____0001 ________
   58|   712k|              CARRY | OVERLONG_2,
   59|       |              // ____001_ ________
   60|   712k|              CARRY, CARRY,
   61|       |
   62|       |              // ____0100 ________
   63|   712k|              CARRY | TOO_LARGE,
   64|       |              // ____0101 ________
   65|   712k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   66|       |              // ____011_ ________
   67|   712k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   68|   712k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   69|       |
   70|       |              // ____1___ ________
   71|   712k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   72|   712k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   73|   712k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   74|   712k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   75|   712k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   76|       |              // ____1101 ________
   77|   712k|              CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
   78|   712k|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   79|   712k|              CARRY | TOO_LARGE | TOO_LARGE_1000);
   80|   712k|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   81|       |      // ________ 0_______ <ASCII in byte 2>
   82|   712k|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   83|   712k|      TOO_SHORT, TOO_SHORT,
   84|       |
   85|       |      // ________ 1000____
   86|   712k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   87|   712k|          OVERLONG_4,
   88|       |      // ________ 1001____
   89|   712k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   90|       |      // ________ 101_____
   91|   712k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   92|   712k|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   93|       |
   94|       |      // ________ 11______
   95|   712k|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   96|   712k|  return (byte_1_high & byte_1_low & byte_2_high);
   97|   712k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3223check_multibyte_lengthsENS1_4simd5simd8IhEES5_S5_:
  101|   712k|                        const simd8<uint8_t> sc) {
  102|   712k|  simd8<uint8_t> prev2 = input.prev<2>(prev_input);
  103|   712k|  simd8<uint8_t> prev3 = input.prev<3>(prev_input);
  104|   712k|  simd8<uint8_t> must23 =
  105|   712k|      simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
  106|   712k|  simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
  107|   712k|  return must23_80 ^ sc;
  108|   712k|}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3221validating_transcoder6errorsEv:
  310|   101k|  simdutf_really_inline bool errors() const {
  311|   101k|    return this->error.any_bits_set_anywhere();
  312|   101k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_113utf8_to_utf3221validating_transcoder19convert_with_errorsEPKcmPDi:
  216|    764|                                                   char32_t *utf32_output) {
  217|    764|    size_t pos = 0;
  218|    764|    char32_t *start{utf32_output};
  219|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  220|       |    // of 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the
  221|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  222|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  223|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  224|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  225|    764|    size_t leading_byte = 0;
  226|    764|    size_t margin = size;
  227|  7.46k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (227:12): [True: 7.21k, False: 254]
  |  Branch (227:26): [True: 6.70k, False: 510]
  ------------------
  228|  6.70k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  229|  6.70k|    }
  230|       |    // If the input is long enough, then we have that margin-1 is the fourth
  231|       |    // last leading byte.
  232|    764|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  233|   498k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (233:12): [True: 498k, False: 688]
  ------------------
  234|   498k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  235|   498k|      if (input.is_ascii()) {
  ------------------
  |  Branch (235:11): [True: 398k, False: 99.9k]
  ------------------
  236|   398k|        input.store_ascii_as_utf32(utf32_output);
  237|   398k|        utf32_output += 64;
  238|   398k|        pos += 64;
  239|   398k|      } else {
  240|       |        // you might think that a for-loop would work, but under Visual Studio,
  241|       |        // it is not good enough.
  242|  99.9k|        static_assert(
  243|  99.9k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  244|  99.9k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  245|  99.9k|            "We support either two or four chunks per 64-byte block.");
  246|  99.9k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  247|  99.9k|        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  248|  99.9k|          this->check_utf8_bytes(input.chunks[0], zero);
  249|  99.9k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  250|       |        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  251|       |          this->check_utf8_bytes(input.chunks[0], zero);
  252|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  253|       |          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  254|       |          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  255|       |        }
  256|  99.9k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  257|  99.9k|        if (errors() || (utf8_continuation_mask & 1)) {
  ------------------
  |  Branch (257:13): [True: 76, False: 99.9k]
  |  Branch (257:25): [True: 0, False: 99.9k]
  ------------------
  258|     76|          result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(
  259|     76|              pos, in + pos, size - pos, utf32_output);
  260|     76|          res.count += pos;
  261|     76|          return res;
  262|     76|        }
  263|  99.9k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  264|  99.9k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  265|       |        // We process in blocks of up to 12 bytes except possibly
  266|       |        // for fast paths which may process up to 16 bytes. For the
  267|       |        // slow path to work, we should have at least 12 input bytes left.
  268|  99.9k|        size_t max_starting_point = (pos + 64) - 12;
  269|       |        // Next loop is going to run at least five times.
  270|   975k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (270:16): [True: 875k, False: 99.9k]
  ------------------
  271|       |          // Performance note: our ability to compute 'consumed' and
  272|       |          // then shift and recompute is critical. If there is a
  273|       |          // latency of, say, 4 cycles on getting 'consumed', then
  274|       |          // the inner loop might have a total latency of about 6 cycles.
  275|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  276|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  277|       |          // for this section of the code. Hence, there is a limit
  278|       |          // to how much we can further increase this latency before
  279|       |          // it seriously harms performance.
  280|   875k|          size_t consumed = convert_masked_utf8_to_utf32(
  281|   875k|              in + pos, utf8_end_of_code_point_mask, utf32_output);
  282|   875k|          pos += consumed;
  283|   875k|          utf8_end_of_code_point_mask >>= consumed;
  284|   875k|        }
  285|       |        // At this point there may remain between 0 and 12 bytes in the
  286|       |        // 64-byte block. These bytes will be processed again. So we have an
  287|       |        // 80% efficiency (in the worst case). In practice we expect an
  288|       |        // 85% to 90% efficiency.
  289|  99.9k|      }
  290|   498k|    }
  291|    688|    if (errors()) {
  ------------------
  |  Branch (291:9): [True: 0, False: 688]
  ------------------
  292|      0|      result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(
  293|      0|          pos, in + pos, size - pos, utf32_output);
  294|      0|      res.count += pos;
  295|      0|      return res;
  296|      0|    }
  297|    688|    if (pos < size) {
  ------------------
  |  Branch (297:9): [True: 684, False: 4]
  ------------------
  298|    684|      result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(
  299|    684|          pos, in + pos, size - pos, utf32_output);
  300|    684|      if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (300:11): [True: 336, False: 348]
  ------------------
  301|    336|        res.count += pos;
  302|    336|        return res;
  303|    348|      } else { // In case of success, we want the number of word written
  304|    348|        utf32_output += res.count;
  305|    348|      }
  306|    684|    }
  307|    352|    return result(error_code::SUCCESS, utf32_output - start);
  308|    688|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3221validating_transcoderC2Ev:
  114|  1.52k|  validating_transcoder() : error(uint8_t(0)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3221validating_transcoder7convertEPKcmPDi:
  129|    758|                                       char32_t *utf32_output) {
  130|    758|    size_t pos = 0;
  131|    758|    char32_t *start{utf32_output};
  132|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  133|       |    // of 8 words when calling convert_masked_utf8_to_utf32. If you skip the
  134|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  135|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  136|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  137|       |    // back from the end counting 16 leading bytes, to give us a good margin.
  138|    758|    size_t leading_byte = 0;
  139|    758|    size_t margin = size;
  140|  7.08k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (140:12): [True: 6.85k, False: 232]
  |  Branch (140:26): [True: 6.32k, False: 526]
  ------------------
  141|  6.32k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  142|  6.32k|    }
  143|       |    // If the input is long enough, then we have that margin-1 is the fourth
  144|       |    // last leading byte.
  145|    758|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  146|   719k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (146:12): [True: 719k, False: 736]
  ------------------
  147|   719k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  148|   719k|      if (input.is_ascii()) {
  ------------------
  |  Branch (148:11): [True: 463k, False: 256k]
  ------------------
  149|   463k|        input.store_ascii_as_utf32(utf32_output);
  150|   463k|        utf32_output += 64;
  151|   463k|        pos += 64;
  152|   463k|      } else {
  153|       |        // you might think that a for-loop would work, but under Visual Studio,
  154|       |        // it is not good enough.
  155|   256k|        static_assert(
  156|   256k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  157|   256k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  158|   256k|            "We support either two or four chunks per 64-byte block.");
  159|   256k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  160|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  161|       |          this->check_utf8_bytes(input.chunks[0], zero);
  162|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  163|   256k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  164|   256k|          this->check_utf8_bytes(input.chunks[0], zero);
  165|   256k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  166|   256k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  167|   256k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  168|   256k|        }
  169|   256k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  170|   256k|        if (utf8_continuation_mask & 1) {
  ------------------
  |  Branch (170:13): [True: 22, False: 256k]
  ------------------
  171|     22|          return 0; // we have an error
  172|     22|        }
  173|   256k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  174|   256k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  175|       |        // We process in blocks of up to 12 bytes except possibly
  176|       |        // for fast paths which may process up to 16 bytes. For the
  177|       |        // slow path to work, we should have at least 12 input bytes left.
  178|   256k|        size_t max_starting_point = (pos + 64) - 12;
  179|       |        // Next loop is going to run at least five times.
  180|  2.54M|        while (pos < max_starting_point) {
  ------------------
  |  Branch (180:16): [True: 2.28M, False: 256k]
  ------------------
  181|       |          // Performance note: our ability to compute 'consumed' and
  182|       |          // then shift and recompute is critical. If there is a
  183|       |          // latency of, say, 4 cycles on getting 'consumed', then
  184|       |          // the inner loop might have a total latency of about 6 cycles.
  185|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  186|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  187|       |          // for this section of the code. Hence, there is a limit
  188|       |          // to how much we can further increase this latency before
  189|       |          // it seriously harms performance.
  190|  2.28M|          size_t consumed = convert_masked_utf8_to_utf32(
  191|  2.28M|              in + pos, utf8_end_of_code_point_mask, utf32_output);
  192|  2.28M|          pos += consumed;
  193|  2.28M|          utf8_end_of_code_point_mask >>= consumed;
  194|  2.28M|        }
  195|       |        // At this point there may remain between 0 and 12 bytes in the
  196|       |        // 64-byte block. These bytes will be processed again. So we have an
  197|       |        // 80% efficiency (in the worst case). In practice we expect an
  198|       |        // 85% to 90% efficiency.
  199|   256k|      }
  200|   719k|    }
  201|    736|    if (errors()) {
  ------------------
  |  Branch (201:9): [True: 274, False: 462]
  ------------------
  202|    274|      return 0;
  203|    274|    }
  204|    462|    if (pos < size) {
  ------------------
  |  Branch (204:9): [True: 458, False: 4]
  ------------------
  205|    458|      size_t howmany =
  206|    458|          scalar::utf8_to_utf32::convert(in + pos, size - pos, utf32_output);
  207|    458|      if (howmany == 0) {
  ------------------
  |  Branch (207:11): [True: 180, False: 278]
  ------------------
  208|    180|        return 0;
  209|    180|      }
  210|    278|      utf32_output += howmany;
  211|    278|    }
  212|    282|    return utf32_output - start;
  213|    462|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3221validating_transcoder16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  119|  1.42M|                                              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.42M|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  124|  1.42M|    simd8<uint8_t> sc = check_special_cases(input, prev1);
  125|  1.42M|    this->error |= check_multibyte_lengths(input, prev_input, sc);
  126|  1.42M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3219check_special_casesENS1_4simd5simd8IhEES5_:
    8|  1.42M|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.42M|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   16|       |                                               // 11______ 11______
   17|  1.42M|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   18|  1.42M|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   19|  1.42M|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   20|  1.42M|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   21|  1.42M|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   22|  1.42M|  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.42M|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   31|       |  // 11110101 1000____
   32|       |  // 1111011_ 1000____
   33|       |  // 11111___ 1000____
   34|  1.42M|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   35|       |
   36|  1.42M|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   37|       |      // 0_______ ________ <ASCII in byte 1>
   38|  1.42M|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   39|  1.42M|      TOO_LONG,
   40|       |      // 10______ ________ <continuation in byte 1>
   41|  1.42M|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   42|       |      // 1100____ ________ <two byte lead in byte 1>
   43|  1.42M|      TOO_SHORT | OVERLONG_2,
   44|       |      // 1101____ ________ <two byte lead in byte 1>
   45|  1.42M|      TOO_SHORT,
   46|       |      // 1110____ ________ <three byte lead in byte 1>
   47|  1.42M|      TOO_SHORT | OVERLONG_3 | SURROGATE,
   48|       |      // 1111____ ________ <four+ byte lead in byte 1>
   49|  1.42M|      TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4);
   50|  1.42M|  constexpr const uint8_t CARRY =
   51|  1.42M|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   52|  1.42M|  const simd8<uint8_t> byte_1_low =
   53|  1.42M|      (prev1 & 0x0F)
   54|  1.42M|          .lookup_16<uint8_t>(
   55|       |              // ____0000 ________
   56|  1.42M|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   57|       |              // ____0001 ________
   58|  1.42M|              CARRY | OVERLONG_2,
   59|       |              // ____001_ ________
   60|  1.42M|              CARRY, CARRY,
   61|       |
   62|       |              // ____0100 ________
   63|  1.42M|              CARRY | TOO_LARGE,
   64|       |              // ____0101 ________
   65|  1.42M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   66|       |              // ____011_ ________
   67|  1.42M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   68|  1.42M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   69|       |
   70|       |              // ____1___ ________
   71|  1.42M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   72|  1.42M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   73|  1.42M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   74|  1.42M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   75|  1.42M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   76|       |              // ____1101 ________
   77|  1.42M|              CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
   78|  1.42M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   79|  1.42M|              CARRY | TOO_LARGE | TOO_LARGE_1000);
   80|  1.42M|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   81|       |      // ________ 0_______ <ASCII in byte 2>
   82|  1.42M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   83|  1.42M|      TOO_SHORT, TOO_SHORT,
   84|       |
   85|       |      // ________ 1000____
   86|  1.42M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   87|  1.42M|          OVERLONG_4,
   88|       |      // ________ 1001____
   89|  1.42M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   90|       |      // ________ 101_____
   91|  1.42M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   92|  1.42M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   93|       |
   94|       |      // ________ 11______
   95|  1.42M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   96|  1.42M|  return (byte_1_high & byte_1_low & byte_2_high);
   97|  1.42M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3223check_multibyte_lengthsENS1_4simd5simd8IhEES5_S5_:
  101|  1.42M|                        const simd8<uint8_t> sc) {
  102|  1.42M|  simd8<uint8_t> prev2 = input.prev<2>(prev_input);
  103|  1.42M|  simd8<uint8_t> prev3 = input.prev<3>(prev_input);
  104|  1.42M|  simd8<uint8_t> must23 =
  105|  1.42M|      simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
  106|  1.42M|  simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
  107|  1.42M|  return must23_80 ^ sc;
  108|  1.42M|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3221validating_transcoder6errorsEv:
  310|   101k|  simdutf_really_inline bool errors() const {
  311|   101k|    return this->error.any_bits_set_anywhere();
  312|   101k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_113utf8_to_utf3221validating_transcoder19convert_with_errorsEPKcmPDi:
  216|    764|                                                   char32_t *utf32_output) {
  217|    764|    size_t pos = 0;
  218|    764|    char32_t *start{utf32_output};
  219|       |    // In the worst case, we have the haswell kernel which can cause an overflow
  220|       |    // of 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the
  221|       |    // last 16 bytes, and if the data is valid, then it is entirely safe because
  222|       |    // 16 UTF-8 bytes generate much more than 8 bytes. However, you cannot
  223|       |    // generally assume that you have valid UTF-8 input, so we are going to go
  224|       |    // back from the end counting 8 leading bytes, to give us a good margin.
  225|    764|    size_t leading_byte = 0;
  226|    764|    size_t margin = size;
  227|  7.46k|    for (; margin > 0 && leading_byte < 8; margin--) {
  ------------------
  |  Branch (227:12): [True: 7.21k, False: 254]
  |  Branch (227:26): [True: 6.70k, False: 510]
  ------------------
  228|  6.70k|      leading_byte += (int8_t(in[margin - 1]) > -65);
  229|  6.70k|    }
  230|       |    // If the input is long enough, then we have that margin-1 is the fourth
  231|       |    // last leading byte.
  232|    764|    const size_t safety_margin = size - margin + 1; // to avoid overruns!
  233|   498k|    while (pos + 64 + safety_margin <= size) {
  ------------------
  |  Branch (233:12): [True: 498k, False: 688]
  ------------------
  234|   498k|      simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
  235|   498k|      if (input.is_ascii()) {
  ------------------
  |  Branch (235:11): [True: 398k, False: 99.9k]
  ------------------
  236|   398k|        input.store_ascii_as_utf32(utf32_output);
  237|   398k|        utf32_output += 64;
  238|   398k|        pos += 64;
  239|   398k|      } else {
  240|       |        // you might think that a for-loop would work, but under Visual Studio,
  241|       |        // it is not good enough.
  242|  99.9k|        static_assert(
  243|  99.9k|            (simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  244|  99.9k|                (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  245|  99.9k|            "We support either two or four chunks per 64-byte block.");
  246|  99.9k|        auto zero = simd8<uint8_t>{uint8_t(0)};
  247|       |        if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  248|       |          this->check_utf8_bytes(input.chunks[0], zero);
  249|       |          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  250|  99.9k|        } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  251|  99.9k|          this->check_utf8_bytes(input.chunks[0], zero);
  252|  99.9k|          this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  253|  99.9k|          this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  254|  99.9k|          this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  255|  99.9k|        }
  256|  99.9k|        uint64_t utf8_continuation_mask = input.lt(-65 + 1);
  257|  99.9k|        if (errors() || (utf8_continuation_mask & 1)) {
  ------------------
  |  Branch (257:13): [True: 76, False: 99.9k]
  |  Branch (257:25): [True: 0, False: 99.9k]
  ------------------
  258|     76|          result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(
  259|     76|              pos, in + pos, size - pos, utf32_output);
  260|     76|          res.count += pos;
  261|     76|          return res;
  262|     76|        }
  263|  99.9k|        uint64_t utf8_leading_mask = ~utf8_continuation_mask;
  264|  99.9k|        uint64_t utf8_end_of_code_point_mask = utf8_leading_mask >> 1;
  265|       |        // We process in blocks of up to 12 bytes except possibly
  266|       |        // for fast paths which may process up to 16 bytes. For the
  267|       |        // slow path to work, we should have at least 12 input bytes left.
  268|  99.9k|        size_t max_starting_point = (pos + 64) - 12;
  269|       |        // Next loop is going to run at least five times.
  270|   975k|        while (pos < max_starting_point) {
  ------------------
  |  Branch (270:16): [True: 875k, False: 99.9k]
  ------------------
  271|       |          // Performance note: our ability to compute 'consumed' and
  272|       |          // then shift and recompute is critical. If there is a
  273|       |          // latency of, say, 4 cycles on getting 'consumed', then
  274|       |          // the inner loop might have a total latency of about 6 cycles.
  275|       |          // Yet we process between 6 to 12 inputs bytes, thus we get
  276|       |          // a speed limit between 1 cycle/byte and 0.5 cycle/byte
  277|       |          // for this section of the code. Hence, there is a limit
  278|       |          // to how much we can further increase this latency before
  279|       |          // it seriously harms performance.
  280|   875k|          size_t consumed = convert_masked_utf8_to_utf32(
  281|   875k|              in + pos, utf8_end_of_code_point_mask, utf32_output);
  282|   875k|          pos += consumed;
  283|   875k|          utf8_end_of_code_point_mask >>= consumed;
  284|   875k|        }
  285|       |        // At this point there may remain between 0 and 12 bytes in the
  286|       |        // 64-byte block. These bytes will be processed again. So we have an
  287|       |        // 80% efficiency (in the worst case). In practice we expect an
  288|       |        // 85% to 90% efficiency.
  289|  99.9k|      }
  290|   498k|    }
  291|    688|    if (errors()) {
  ------------------
  |  Branch (291:9): [True: 0, False: 688]
  ------------------
  292|      0|      result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(
  293|      0|          pos, in + pos, size - pos, utf32_output);
  294|      0|      res.count += pos;
  295|      0|      return res;
  296|      0|    }
  297|    688|    if (pos < size) {
  ------------------
  |  Branch (297:9): [True: 684, False: 4]
  ------------------
  298|    684|      result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(
  299|    684|          pos, in + pos, size - pos, utf32_output);
  300|    684|      if (res.error) { // In case of error, we want the error position
  ------------------
  |  Branch (300:11): [True: 336, False: 348]
  ------------------
  301|    336|        res.count += pos;
  302|    336|        return res;
  303|    348|      } else { // In case of success, we want the number of word written
  304|    348|        utf32_output += res.count;
  305|    348|      }
  306|    684|    }
  307|    352|    return result(error_code::SUCCESS, utf32_output - start);
  308|    688|  }

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

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation12utf8_checker16check_next_inputERKNS1_4simd8simd8x64IhEE:
  187|  4.98M|  simdutf_really_inline void check_next_input(const simd8x64<uint8_t> &input) {
  188|  4.98M|    if (simdutf_likely(is_ascii(input))) {
  ------------------
  |  |   89|  4.98M|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (89:31): [True: 4.06M, False: 920k]
  |  |  ------------------
  ------------------
  189|  4.06M|      this->error |= this->prev_incomplete;
  190|  4.06M|    } else {
  191|       |      // you might think that a for-loop would work, but under Visual Studio, it
  192|       |      // is not good enough.
  193|   920k|      static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  194|   920k|                        (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  195|   920k|                    "We support either two or four chunks per 64-byte block.");
  196|   920k|      if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  197|   920k|        this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
  198|   920k|        this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  199|       |      } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  200|       |        this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
  201|       |        this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  202|       |        this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  203|       |        this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  204|       |      }
  205|   920k|      this->prev_incomplete =
  206|   920k|          is_incomplete(input.chunks[simd8x64<uint8_t>::NUM_CHUNKS - 1]);
  207|   920k|      this->prev_input_block = input.chunks[simd8x64<uint8_t>::NUM_CHUNKS - 1];
  208|   920k|    }
  209|  4.98M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation12utf8_checker16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  169|  1.84M|                                              const simd8<uint8_t> prev_input) {
  170|       |    // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+
  171|       |    // lead bytes (2, 3, 4-byte leads become large positive numbers instead of
  172|       |    // small negative numbers)
  173|  1.84M|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  174|  1.84M|    simd8<uint8_t> sc = check_special_cases(input, prev1);
  175|  1.84M|    this->error |= check_multibyte_lengths(input, prev_input, sc);
  176|  1.84M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation19check_special_casesENS1_4simd5simd8IhEES5_:
    9|  1.84M|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|  1.84M|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   17|       |                                               // 11______ 11______
   18|  1.84M|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   19|  1.84M|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   20|  1.84M|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   21|  1.84M|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   22|  1.84M|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   23|  1.84M|  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|  1.84M|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   32|       |  // 11110101 1000____
   33|       |  // 1111011_ 1000____
   34|       |  // 11111___ 1000____
   35|  1.84M|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   36|       |
   37|  1.84M|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   38|       |      // 0_______ ________ <ASCII in byte 1>
   39|  1.84M|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   40|  1.84M|      TOO_LONG,
   41|       |      // 10______ ________ <continuation in byte 1>
   42|  1.84M|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   43|       |      // 1100____ ________ <two byte lead in byte 1>
   44|  1.84M|      TOO_SHORT | OVERLONG_2,
   45|       |      // 1101____ ________ <two byte lead in byte 1>
   46|  1.84M|      TOO_SHORT,
   47|       |      // 1110____ ________ <three byte lead in byte 1>
   48|  1.84M|      TOO_SHORT | OVERLONG_3 | SURROGATE,
   49|       |      // 1111____ ________ <four+ byte lead in byte 1>
   50|  1.84M|      TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4);
   51|  1.84M|  constexpr const uint8_t CARRY =
   52|  1.84M|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   53|  1.84M|  const simd8<uint8_t> byte_1_low =
   54|  1.84M|      (prev1 & 0x0F)
   55|  1.84M|          .lookup_16<uint8_t>(
   56|       |              // ____0000 ________
   57|  1.84M|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   58|       |              // ____0001 ________
   59|  1.84M|              CARRY | OVERLONG_2,
   60|       |              // ____001_ ________
   61|  1.84M|              CARRY, CARRY,
   62|       |
   63|       |              // ____0100 ________
   64|  1.84M|              CARRY | TOO_LARGE,
   65|       |              // ____0101 ________
   66|  1.84M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   67|       |              // ____011_ ________
   68|  1.84M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   69|  1.84M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   70|       |
   71|       |              // ____1___ ________
   72|  1.84M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   73|  1.84M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   74|  1.84M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   75|  1.84M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   76|  1.84M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   77|       |              // ____1101 ________
   78|  1.84M|              CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
   79|  1.84M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   80|  1.84M|              CARRY | TOO_LARGE | TOO_LARGE_1000);
   81|  1.84M|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   82|       |      // ________ 0_______ <ASCII in byte 2>
   83|  1.84M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   84|  1.84M|      TOO_SHORT, TOO_SHORT,
   85|       |
   86|       |      // ________ 1000____
   87|  1.84M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   88|  1.84M|          OVERLONG_4,
   89|       |      // ________ 1001____
   90|  1.84M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   91|       |      // ________ 101_____
   92|  1.84M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   93|  1.84M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   94|       |
   95|       |      // ________ 11______
   96|  1.84M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   97|  1.84M|  return (byte_1_high & byte_1_low & byte_2_high);
   98|  1.84M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation23check_multibyte_lengthsENS1_4simd5simd8IhEES5_S5_:
  102|  1.84M|                        const simd8<uint8_t> sc) {
  103|  1.84M|  simd8<uint8_t> prev2 = input.prev<2>(prev_input);
  104|  1.84M|  simd8<uint8_t> prev3 = input.prev<3>(prev_input);
  105|  1.84M|  simd8<uint8_t> must23 =
  106|  1.84M|      simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
  107|  1.84M|  simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
  108|  1.84M|  return must23_80 ^ sc;
  109|  1.84M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation13is_incompleteENS1_4simd5simd8IhEE:
  115|   920k|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|   920k|  static const uint8_t max_array[32] = {255,
  120|   920k|                                        255,
  121|   920k|                                        255,
  122|   920k|                                        255,
  123|   920k|                                        255,
  124|   920k|                                        255,
  125|   920k|                                        255,
  126|   920k|                                        255,
  127|   920k|                                        255,
  128|   920k|                                        255,
  129|   920k|                                        255,
  130|   920k|                                        255,
  131|   920k|                                        255,
  132|   920k|                                        255,
  133|   920k|                                        255,
  134|   920k|                                        255,
  135|   920k|                                        255,
  136|   920k|                                        255,
  137|   920k|                                        255,
  138|   920k|                                        255,
  139|   920k|                                        255,
  140|   920k|                                        255,
  141|   920k|                                        255,
  142|   920k|                                        255,
  143|   920k|                                        255,
  144|   920k|                                        255,
  145|   920k|                                        255,
  146|   920k|                                        255,
  147|   920k|                                        255,
  148|   920k|                                        0b11110000u - 1,
  149|   920k|                                        0b11100000u - 1,
  150|   920k|                                        0b11000000u - 1};
  151|   920k|  const simd8<uint8_t> max_value(
  152|   920k|      &max_array[sizeof(max_array) - sizeof(simd8<uint8_t>)]);
  153|   920k|  return input.gt_bits(max_value);
  154|   920k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation12utf8_checker9check_eofEv:
  181|  5.98k|  simdutf_really_inline void check_eof() {
  182|       |    // If the previous block had incomplete UTF-8 characters at the end, an
  183|       |    // ASCII block can't possibly finish them.
  184|  5.98k|    this->error |= this->prev_incomplete;
  185|  5.98k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_115utf8_validation12utf8_checker6errorsEv:
  212|  2.08M|  simdutf_really_inline bool errors() const {
  213|  2.08M|    return this->error.any_bits_set_anywhere();
  214|  2.08M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation12utf8_checker16check_next_inputERKNS1_4simd8simd8x64IhEE:
  187|  4.98M|  simdutf_really_inline void check_next_input(const simd8x64<uint8_t> &input) {
  188|  4.98M|    if (simdutf_likely(is_ascii(input))) {
  ------------------
  |  |   89|  4.98M|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (89:31): [True: 4.06M, False: 920k]
  |  |  ------------------
  ------------------
  189|  4.06M|      this->error |= this->prev_incomplete;
  190|  4.06M|    } else {
  191|       |      // you might think that a for-loop would work, but under Visual Studio, it
  192|       |      // is not good enough.
  193|   920k|      static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) ||
  194|   920k|                        (simd8x64<uint8_t>::NUM_CHUNKS == 4),
  195|   920k|                    "We support either two or four chunks per 64-byte block.");
  196|       |      if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 2) {
  197|       |        this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
  198|       |        this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  199|   920k|      } else if constexpr (simd8x64<uint8_t>::NUM_CHUNKS == 4) {
  200|   920k|        this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
  201|   920k|        this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
  202|   920k|        this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
  203|   920k|        this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
  204|   920k|      }
  205|   920k|      this->prev_incomplete =
  206|   920k|          is_incomplete(input.chunks[simd8x64<uint8_t>::NUM_CHUNKS - 1]);
  207|   920k|      this->prev_input_block = input.chunks[simd8x64<uint8_t>::NUM_CHUNKS - 1];
  208|   920k|    }
  209|  4.98M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation12utf8_checker16check_utf8_bytesENS1_4simd5simd8IhEES6_:
  169|  3.68M|                                              const simd8<uint8_t> prev_input) {
  170|       |    // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+
  171|       |    // lead bytes (2, 3, 4-byte leads become large positive numbers instead of
  172|       |    // small negative numbers)
  173|  3.68M|    simd8<uint8_t> prev1 = input.prev<1>(prev_input);
  174|  3.68M|    simd8<uint8_t> sc = check_special_cases(input, prev1);
  175|  3.68M|    this->error |= check_multibyte_lengths(input, prev_input, sc);
  176|  3.68M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation19check_special_casesENS1_4simd5simd8IhEES5_:
    9|  3.68M|check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
   10|       |  // Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
   11|       |  // Bit 1 = Too Long (ASCII followed by continuation)
   12|       |  // Bit 2 = Overlong 3-byte
   13|       |  // Bit 4 = Surrogate
   14|       |  // Bit 5 = Overlong 2-byte
   15|       |  // Bit 7 = Two Continuations
   16|  3.68M|  constexpr const uint8_t TOO_SHORT = 1 << 0;  // 11______ 0_______
   17|       |                                               // 11______ 11______
   18|  3.68M|  constexpr const uint8_t TOO_LONG = 1 << 1;   // 0_______ 10______
   19|  3.68M|  constexpr const uint8_t OVERLONG_3 = 1 << 2; // 11100000 100_____
   20|  3.68M|  constexpr const uint8_t SURROGATE = 1 << 4;  // 11101101 101_____
   21|  3.68M|  constexpr const uint8_t OVERLONG_2 = 1 << 5; // 1100000_ 10______
   22|  3.68M|  constexpr const uint8_t TWO_CONTS = 1 << 7;  // 10______ 10______
   23|  3.68M|  constexpr const uint8_t TOO_LARGE = 1 << 3;  // 11110100 1001____
   24|       |                                               // 11110100 101_____
   25|       |                                               // 11110101 1001____
   26|       |                                               // 11110101 101_____
   27|       |                                               // 1111011_ 1001____
   28|       |                                               // 1111011_ 101_____
   29|       |                                               // 11111___ 1001____
   30|       |                                               // 11111___ 101_____
   31|  3.68M|  constexpr const uint8_t TOO_LARGE_1000 = 1 << 6;
   32|       |  // 11110101 1000____
   33|       |  // 1111011_ 1000____
   34|       |  // 11111___ 1000____
   35|  3.68M|  constexpr const uint8_t OVERLONG_4 = 1 << 6; // 11110000 1000____
   36|       |
   37|  3.68M|  const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
   38|       |      // 0_______ ________ <ASCII in byte 1>
   39|  3.68M|      TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
   40|  3.68M|      TOO_LONG,
   41|       |      // 10______ ________ <continuation in byte 1>
   42|  3.68M|      TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
   43|       |      // 1100____ ________ <two byte lead in byte 1>
   44|  3.68M|      TOO_SHORT | OVERLONG_2,
   45|       |      // 1101____ ________ <two byte lead in byte 1>
   46|  3.68M|      TOO_SHORT,
   47|       |      // 1110____ ________ <three byte lead in byte 1>
   48|  3.68M|      TOO_SHORT | OVERLONG_3 | SURROGATE,
   49|       |      // 1111____ ________ <four+ byte lead in byte 1>
   50|  3.68M|      TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4);
   51|  3.68M|  constexpr const uint8_t CARRY =
   52|  3.68M|      TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
   53|  3.68M|  const simd8<uint8_t> byte_1_low =
   54|  3.68M|      (prev1 & 0x0F)
   55|  3.68M|          .lookup_16<uint8_t>(
   56|       |              // ____0000 ________
   57|  3.68M|              CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
   58|       |              // ____0001 ________
   59|  3.68M|              CARRY | OVERLONG_2,
   60|       |              // ____001_ ________
   61|  3.68M|              CARRY, CARRY,
   62|       |
   63|       |              // ____0100 ________
   64|  3.68M|              CARRY | TOO_LARGE,
   65|       |              // ____0101 ________
   66|  3.68M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   67|       |              // ____011_ ________
   68|  3.68M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   69|  3.68M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   70|       |
   71|       |              // ____1___ ________
   72|  3.68M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   73|  3.68M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   74|  3.68M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   75|  3.68M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   76|  3.68M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   77|       |              // ____1101 ________
   78|  3.68M|              CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
   79|  3.68M|              CARRY | TOO_LARGE | TOO_LARGE_1000,
   80|  3.68M|              CARRY | TOO_LARGE | TOO_LARGE_1000);
   81|  3.68M|  const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
   82|       |      // ________ 0_______ <ASCII in byte 2>
   83|  3.68M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
   84|  3.68M|      TOO_SHORT, TOO_SHORT,
   85|       |
   86|       |      // ________ 1000____
   87|  3.68M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 |
   88|  3.68M|          OVERLONG_4,
   89|       |      // ________ 1001____
   90|  3.68M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
   91|       |      // ________ 101_____
   92|  3.68M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   93|  3.68M|      TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
   94|       |
   95|       |      // ________ 11______
   96|  3.68M|      TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT);
   97|  3.68M|  return (byte_1_high & byte_1_low & byte_2_high);
   98|  3.68M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation23check_multibyte_lengthsENS1_4simd5simd8IhEES5_S5_:
  102|  3.68M|                        const simd8<uint8_t> sc) {
  103|  3.68M|  simd8<uint8_t> prev2 = input.prev<2>(prev_input);
  104|  3.68M|  simd8<uint8_t> prev3 = input.prev<3>(prev_input);
  105|  3.68M|  simd8<uint8_t> must23 =
  106|  3.68M|      simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
  107|  3.68M|  simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
  108|  3.68M|  return must23_80 ^ sc;
  109|  3.68M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation13is_incompleteENS1_4simd5simd8IhEE:
  115|   920k|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|   920k|  static const uint8_t max_array[32] = {255,
  120|   920k|                                        255,
  121|   920k|                                        255,
  122|   920k|                                        255,
  123|   920k|                                        255,
  124|   920k|                                        255,
  125|   920k|                                        255,
  126|   920k|                                        255,
  127|   920k|                                        255,
  128|   920k|                                        255,
  129|   920k|                                        255,
  130|   920k|                                        255,
  131|   920k|                                        255,
  132|   920k|                                        255,
  133|   920k|                                        255,
  134|   920k|                                        255,
  135|   920k|                                        255,
  136|   920k|                                        255,
  137|   920k|                                        255,
  138|   920k|                                        255,
  139|   920k|                                        255,
  140|   920k|                                        255,
  141|   920k|                                        255,
  142|   920k|                                        255,
  143|   920k|                                        255,
  144|   920k|                                        255,
  145|   920k|                                        255,
  146|   920k|                                        255,
  147|   920k|                                        255,
  148|   920k|                                        0b11110000u - 1,
  149|   920k|                                        0b11100000u - 1,
  150|   920k|                                        0b11000000u - 1};
  151|   920k|  const simd8<uint8_t> max_value(
  152|   920k|      &max_array[sizeof(max_array) - sizeof(simd8<uint8_t>)]);
  153|   920k|  return input.gt_bits(max_value);
  154|   920k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation12utf8_checker9check_eofEv:
  181|  5.98k|  simdutf_really_inline void check_eof() {
  182|       |    // If the previous block had incomplete UTF-8 characters at the end, an
  183|       |    // ASCII block can't possibly finish them.
  184|  5.98k|    this->error |= this->prev_incomplete;
  185|  5.98k|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_115utf8_validation12utf8_checker6errorsEv:
  212|  2.08M|  simdutf_really_inline bool errors() const {
  213|  2.08M|    return this->error.any_bits_set_anywhere();
  214|  2.08M|  }

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation21generic_validate_utf8EPKcm:
   27|  3.42k|bool generic_validate_utf8(const char *input, size_t length) {
   28|  3.42k|  return generic_validate_utf8<utf8_checker>(
   29|  3.42k|      reinterpret_cast<const uint8_t *>(input), length);
   30|  3.42k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation21generic_validate_utf8INS2_12utf8_checkerEEEbPKhm:
   10|  3.42k|bool generic_validate_utf8(const uint8_t *input, size_t length) {
   11|  3.42k|  checker c{};
   12|  3.42k|  buf_block_reader<64> reader(input, length);
   13|  2.90M|  while (reader.has_full_block()) {
  ------------------
  |  Branch (13:10): [True: 2.90M, False: 3.42k]
  ------------------
   14|  2.90M|    simd::simd8x64<uint8_t> in(reader.full_block());
   15|  2.90M|    c.check_next_input(in);
   16|  2.90M|    reader.advance();
   17|  2.90M|  }
   18|  3.42k|  uint8_t block[64]{};
   19|  3.42k|  reader.get_remainder(block);
   20|  3.42k|  simd::simd8x64<uint8_t> in(block);
   21|  3.42k|  c.check_next_input(in);
   22|  3.42k|  reader.advance();
   23|  3.42k|  c.check_eof();
   24|  3.42k|  return !c.errors();
   25|  3.42k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation33generic_validate_utf8_with_errorsEPKcm:
   76|  3.42k|result generic_validate_utf8_with_errors(const char *input, size_t length) {
   77|  3.42k|  return generic_validate_utf8_with_errors<utf8_checker>(
   78|  3.42k|      reinterpret_cast<const uint8_t *>(input), length);
   79|  3.42k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_115utf8_validation33generic_validate_utf8_with_errorsINS2_12utf8_checkerEEENS_6resultEPKhm:
   36|  3.42k|result generic_validate_utf8_with_errors(const uint8_t *input, size_t length) {
   37|  3.42k|  checker c{};
   38|  3.42k|  buf_block_reader<64> reader(input, length);
   39|  3.42k|  size_t count{0};
   40|  2.07M|  while (reader.has_full_block()) {
  ------------------
  |  Branch (40:10): [True: 2.07M, False: 2.56k]
  ------------------
   41|  2.07M|    simd::simd8x64<uint8_t> in(reader.full_block());
   42|  2.07M|    c.check_next_input(in);
   43|  2.07M|    if (c.errors()) {
  ------------------
  |  Branch (43:9): [True: 860, False: 2.07M]
  ------------------
   44|    860|      if (count != 0) {
  ------------------
  |  Branch (44:11): [True: 216, False: 644]
  ------------------
   45|    216|        count--;
   46|    216|      } // Sometimes the error is only detected in the next chunk
   47|    860|      result res = scalar::utf8::rewind_and_validate_with_errors(
   48|    860|          reinterpret_cast<const char *>(input),
   49|    860|          reinterpret_cast<const char *>(input + count), length - count);
   50|    860|      res.count += count;
   51|    860|      return res;
   52|    860|    }
   53|  2.07M|    reader.advance();
   54|  2.07M|    count += 64;
   55|  2.07M|  }
   56|  2.56k|  uint8_t block[64]{};
   57|  2.56k|  reader.get_remainder(block);
   58|  2.56k|  simd::simd8x64<uint8_t> in(block);
   59|  2.56k|  c.check_next_input(in);
   60|  2.56k|  reader.advance();
   61|  2.56k|  c.check_eof();
   62|  2.56k|  if (c.errors()) {
  ------------------
  |  Branch (62:7): [True: 842, False: 1.72k]
  ------------------
   63|    842|    if (count != 0) {
  ------------------
  |  Branch (63:9): [True: 222, False: 620]
  ------------------
   64|    222|      count--;
   65|    222|    } // Sometimes the error is only detected in the next chunk
   66|    842|    result res = scalar::utf8::rewind_and_validate_with_errors(
   67|    842|        reinterpret_cast<const char *>(input),
   68|    842|        reinterpret_cast<const char *>(input) + count, length - count);
   69|    842|    res.count += count;
   70|    842|    return res;
   71|  1.72k|  } else {
   72|  1.72k|    return result(error_code::SUCCESS, length);
   73|  1.72k|  }
   74|  2.56k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation21generic_validate_utf8EPKcm:
   27|  3.42k|bool generic_validate_utf8(const char *input, size_t length) {
   28|  3.42k|  return generic_validate_utf8<utf8_checker>(
   29|  3.42k|      reinterpret_cast<const uint8_t *>(input), length);
   30|  3.42k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation21generic_validate_utf8INS2_12utf8_checkerEEEbPKhm:
   10|  3.42k|bool generic_validate_utf8(const uint8_t *input, size_t length) {
   11|  3.42k|  checker c{};
   12|  3.42k|  buf_block_reader<64> reader(input, length);
   13|  2.90M|  while (reader.has_full_block()) {
  ------------------
  |  Branch (13:10): [True: 2.90M, False: 3.42k]
  ------------------
   14|  2.90M|    simd::simd8x64<uint8_t> in(reader.full_block());
   15|  2.90M|    c.check_next_input(in);
   16|  2.90M|    reader.advance();
   17|  2.90M|  }
   18|  3.42k|  uint8_t block[64]{};
   19|  3.42k|  reader.get_remainder(block);
   20|  3.42k|  simd::simd8x64<uint8_t> in(block);
   21|  3.42k|  c.check_next_input(in);
   22|  3.42k|  reader.advance();
   23|  3.42k|  c.check_eof();
   24|  3.42k|  return !c.errors();
   25|  3.42k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation33generic_validate_utf8_with_errorsEPKcm:
   76|  3.42k|result generic_validate_utf8_with_errors(const char *input, size_t length) {
   77|  3.42k|  return generic_validate_utf8_with_errors<utf8_checker>(
   78|  3.42k|      reinterpret_cast<const uint8_t *>(input), length);
   79|  3.42k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_115utf8_validation33generic_validate_utf8_with_errorsINS2_12utf8_checkerEEENS_6resultEPKhm:
   36|  3.42k|result generic_validate_utf8_with_errors(const uint8_t *input, size_t length) {
   37|  3.42k|  checker c{};
   38|  3.42k|  buf_block_reader<64> reader(input, length);
   39|  3.42k|  size_t count{0};
   40|  2.07M|  while (reader.has_full_block()) {
  ------------------
  |  Branch (40:10): [True: 2.07M, False: 2.56k]
  ------------------
   41|  2.07M|    simd::simd8x64<uint8_t> in(reader.full_block());
   42|  2.07M|    c.check_next_input(in);
   43|  2.07M|    if (c.errors()) {
  ------------------
  |  Branch (43:9): [True: 860, False: 2.07M]
  ------------------
   44|    860|      if (count != 0) {
  ------------------
  |  Branch (44:11): [True: 216, False: 644]
  ------------------
   45|    216|        count--;
   46|    216|      } // Sometimes the error is only detected in the next chunk
   47|    860|      result res = scalar::utf8::rewind_and_validate_with_errors(
   48|    860|          reinterpret_cast<const char *>(input),
   49|    860|          reinterpret_cast<const char *>(input + count), length - count);
   50|    860|      res.count += count;
   51|    860|      return res;
   52|    860|    }
   53|  2.07M|    reader.advance();
   54|  2.07M|    count += 64;
   55|  2.07M|  }
   56|  2.56k|  uint8_t block[64]{};
   57|  2.56k|  reader.get_remainder(block);
   58|  2.56k|  simd::simd8x64<uint8_t> in(block);
   59|  2.56k|  c.check_next_input(in);
   60|  2.56k|  reader.advance();
   61|  2.56k|  c.check_eof();
   62|  2.56k|  if (c.errors()) {
  ------------------
  |  Branch (62:7): [True: 842, False: 1.72k]
  ------------------
   63|    842|    if (count != 0) {
  ------------------
  |  Branch (63:9): [True: 222, False: 620]
  ------------------
   64|    222|      count--;
   65|    222|    } // Sometimes the error is only detected in the next chunk
   66|    842|    result res = scalar::utf8::rewind_and_validate_with_errors(
   67|    842|        reinterpret_cast<const char *>(input),
   68|    842|        reinterpret_cast<const char *>(input) + count, length - count);
   69|    842|    res.count += count;
   70|    842|    return res;
   71|  1.72k|  } else {
   72|  1.72k|    return result(error_code::SUCCESS, length);
   73|  1.72k|  }
   74|  2.56k|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1626validate_utf16_with_errorsILNS_10endiannessE0EEEKNS_6resultEPKDsm:
   49|  2.76k|const result validate_utf16_with_errors(const char16_t *input, size_t size) {
   50|  2.76k|  if (simdutf_unlikely(size == 0)) {
  ------------------
  |  |   92|  2.76k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (92:33): [True: 16, False: 2.75k]
  |  |  ------------------
  ------------------
   51|     16|    return result(error_code::SUCCESS, 0);
   52|     16|  }
   53|       |
   54|  2.75k|  const char16_t *start = input;
   55|  2.75k|  const char16_t *end = input + size;
   56|       |
   57|  2.75k|  const auto v_d8 = simd8<uint8_t>::splat(0xd8);
   58|  2.75k|  const auto v_f8 = simd8<uint8_t>::splat(0xf8);
   59|  2.75k|  const auto v_fc = simd8<uint8_t>::splat(0xfc);
   60|  2.75k|  const auto v_dc = simd8<uint8_t>::splat(0xdc);
   61|       |
   62|  2.59M|  while (input + simd16<uint16_t>::SIZE * 2 < end) {
  ------------------
  |  Branch (62:10): [True: 2.59M, False: 2.50k]
  ------------------
   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.59M|    auto in0 = simd16<uint16_t>(input);
   67|  2.59M|    auto in1 =
   68|  2.59M|        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.59M|    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.59M|    const auto surrogates_wordmask = (in & v_f8) == v_d8;
   76|  2.59M|    const uint16_t surrogates_bitmask =
   77|  2.59M|        static_cast<uint16_t>(surrogates_wordmask.to_bitmask());
   78|  2.59M|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (78:9): [True: 2.56M, False: 26.8k]
  ------------------
   79|  2.56M|      input += 16;
   80|  2.56M|    } 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.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|  26.8k|      const auto vH = (in & v_fc) == v_dc;
   93|  26.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|  26.8k|      const uint16_t L = static_cast<uint16_t>(~H & surrogates_bitmask);
   98|       |
   99|  26.8k|      const uint16_t a = static_cast<uint16_t>(
  100|  26.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|  26.8k|      const uint16_t b = static_cast<uint16_t>(
  104|  26.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|  26.8k|      const uint16_t c = static_cast<uint16_t>(
  107|  26.8k|          V | a | b); // Combine all the masks into the final one.
  108|       |
  109|  26.8k|      if (c == 0xffff) {
  ------------------
  |  Branch (109:11): [True: 19.9k, False: 6.90k]
  ------------------
  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.65k, False: 248]
  ------------------
  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.65k|        input += 15;
  119|  6.65k|      } else {
  120|    248|        return result(error_code::SURROGATE, input - start);
  121|    248|      }
  122|  26.8k|    }
  123|  2.59M|  }
  124|       |
  125|  2.50k|  return result(error_code::SUCCESS, input - start);
  126|  2.75k|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_15utf1626validate_utf16_with_errorsILNS_10endiannessE1EEEKNS_6resultEPKDsm:
   49|  2.59k|const result validate_utf16_with_errors(const char16_t *input, size_t size) {
   50|  2.59k|  if (simdutf_unlikely(size == 0)) {
  ------------------
  |  |   92|  2.59k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (92:33): [True: 17, False: 2.57k]
  |  |  ------------------
  ------------------
   51|     17|    return result(error_code::SUCCESS, 0);
   52|     17|  }
   53|       |
   54|  2.57k|  const char16_t *start = input;
   55|  2.57k|  const char16_t *end = input + size;
   56|       |
   57|  2.57k|  const auto v_d8 = simd8<uint8_t>::splat(0xd8);
   58|  2.57k|  const auto v_f8 = simd8<uint8_t>::splat(0xf8);
   59|  2.57k|  const auto v_fc = simd8<uint8_t>::splat(0xfc);
   60|  2.57k|  const auto v_dc = simd8<uint8_t>::splat(0xdc);
   61|       |
   62|  2.67M|  while (input + simd16<uint16_t>::SIZE * 2 < end) {
  ------------------
  |  Branch (62:10): [True: 2.67M, False: 2.36k]
  ------------------
   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.67M|    auto in0 = simd16<uint16_t>(input);
   67|  2.67M|    auto in1 =
   68|  2.67M|        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.67M|    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.67M|    const auto surrogates_wordmask = (in & v_f8) == v_d8;
   76|  2.67M|    const uint16_t surrogates_bitmask =
   77|  2.67M|        static_cast<uint16_t>(surrogates_wordmask.to_bitmask());
   78|  2.67M|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (78:9): [True: 2.63M, False: 32.5k]
  ------------------
   79|  2.63M|      input += 16;
   80|  2.63M|    } 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|  32.5k|      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|  32.5k|      const auto vH = (in & v_fc) == v_dc;
   93|  32.5k|      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|  32.5k|      const uint16_t L = static_cast<uint16_t>(~H & surrogates_bitmask);
   98|       |
   99|  32.5k|      const uint16_t a = static_cast<uint16_t>(
  100|  32.5k|          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|  32.5k|      const uint16_t b = static_cast<uint16_t>(
  104|  32.5k|          a << 1); // Just mark that the opinput - startite fact is hold,
  105|       |                   // thanks to that we have only two masks for valid case.
  106|  32.5k|      const uint16_t c = static_cast<uint16_t>(
  107|  32.5k|          V | a | b); // Combine all the masks into the final one.
  108|       |
  109|  32.5k|      if (c == 0xffff) {
  ------------------
  |  Branch (109:11): [True: 27.2k, False: 5.39k]
  ------------------
  110|       |        // The whole input register contains valid UTF-16, i.e.,
  111|       |        // either single code units or proper surrogate pairs.
  112|  27.2k|        input += 16;
  113|  27.2k|      } else if (c == 0x7fff) {
  ------------------
  |  Branch (113:18): [True: 5.18k, False: 212]
  ------------------
  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|  5.18k|        input += 15;
  119|  5.18k|      } else {
  120|    212|        return result(error_code::SURROGATE, input - start);
  121|    212|      }
  122|  32.5k|    }
  123|  2.67M|  }
  124|       |
  125|  2.36k|  return result(error_code::SUCCESS, input - start);
  126|  2.57k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1626validate_utf16_with_errorsILNS_10endiannessE0EEEKNS_6resultEPKDsm:
   49|  2.76k|const result validate_utf16_with_errors(const char16_t *input, size_t size) {
   50|  2.76k|  if (simdutf_unlikely(size == 0)) {
  ------------------
  |  |   92|  2.76k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (92:33): [True: 16, False: 2.75k]
  |  |  ------------------
  ------------------
   51|     16|    return result(error_code::SUCCESS, 0);
   52|     16|  }
   53|       |
   54|  2.75k|  const char16_t *start = input;
   55|  2.75k|  const char16_t *end = input + size;
   56|       |
   57|  2.75k|  const auto v_d8 = simd8<uint8_t>::splat(0xd8);
   58|  2.75k|  const auto v_f8 = simd8<uint8_t>::splat(0xf8);
   59|  2.75k|  const auto v_fc = simd8<uint8_t>::splat(0xfc);
   60|  2.75k|  const auto v_dc = simd8<uint8_t>::splat(0xdc);
   61|       |
   62|  2.60M|  while (input + simd16<uint16_t>::SIZE * 2 < end) {
  ------------------
  |  Branch (62:10): [True: 2.59M, 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.59M|    auto in0 = simd16<uint16_t>(input);
   67|  2.59M|    auto in1 =
   68|  2.59M|        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.59M|    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.59M|    const auto surrogates_wordmask = (in & v_f8) == v_d8;
   76|  2.59M|    const uint16_t surrogates_bitmask =
   77|  2.59M|        static_cast<uint16_t>(surrogates_wordmask.to_bitmask());
   78|  2.59M|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (78:9): [True: 2.57M, False: 27.3k]
  ------------------
   79|  2.57M|      input += 16;
   80|  2.57M|    } 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|  27.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|  27.3k|      const auto vH = (in & v_fc) == v_dc;
   93|  27.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|  27.3k|      const uint16_t L = static_cast<uint16_t>(~H & surrogates_bitmask);
   98|       |
   99|  27.3k|      const uint16_t a = static_cast<uint16_t>(
  100|  27.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|  27.3k|      const uint16_t b = static_cast<uint16_t>(
  104|  27.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|  27.3k|      const uint16_t c = static_cast<uint16_t>(
  107|  27.3k|          V | a | b); // Combine all the masks into the final one.
  108|       |
  109|  27.3k|      if (c == 0xffff) {
  ------------------
  |  Branch (109:11): [True: 20.3k, False: 7.07k]
  ------------------
  110|       |        // The whole input register contains valid UTF-16, i.e.,
  111|       |        // either single code units or proper surrogate pairs.
  112|  20.3k|        input += 16;
  113|  20.3k|      } else if (c == 0x7fff) {
  ------------------
  |  Branch (113:18): [True: 6.74k, False: 332]
  ------------------
  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.74k|        input += 15;
  119|  6.74k|      } else {
  120|    332|        return result(error_code::SURROGATE, input - start);
  121|    332|      }
  122|  27.3k|    }
  123|  2.59M|  }
  124|       |
  125|  2.41k|  return result(error_code::SUCCESS, input - start);
  126|  2.75k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_15utf1626validate_utf16_with_errorsILNS_10endiannessE1EEEKNS_6resultEPKDsm:
   49|  2.59k|const result validate_utf16_with_errors(const char16_t *input, size_t size) {
   50|  2.59k|  if (simdutf_unlikely(size == 0)) {
  ------------------
  |  |   92|  2.59k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (92:33): [True: 17, False: 2.57k]
  |  |  ------------------
  ------------------
   51|     17|    return result(error_code::SUCCESS, 0);
   52|     17|  }
   53|       |
   54|  2.57k|  const char16_t *start = input;
   55|  2.57k|  const char16_t *end = input + size;
   56|       |
   57|  2.57k|  const auto v_d8 = simd8<uint8_t>::splat(0xd8);
   58|  2.57k|  const auto v_f8 = simd8<uint8_t>::splat(0xf8);
   59|  2.57k|  const auto v_fc = simd8<uint8_t>::splat(0xfc);
   60|  2.57k|  const auto v_dc = simd8<uint8_t>::splat(0xdc);
   61|       |
   62|  2.67M|  while (input + simd16<uint16_t>::SIZE * 2 < end) {
  ------------------
  |  Branch (62:10): [True: 2.67M, False: 2.32k]
  ------------------
   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.67M|    auto in0 = simd16<uint16_t>(input);
   67|  2.67M|    auto in1 =
   68|  2.67M|        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.67M|    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.67M|    const auto surrogates_wordmask = (in & v_f8) == v_d8;
   76|  2.67M|    const uint16_t surrogates_bitmask =
   77|  2.67M|        static_cast<uint16_t>(surrogates_wordmask.to_bitmask());
   78|  2.67M|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (78:9): [True: 2.63M, False: 33.0k]
  ------------------
   79|  2.63M|      input += 16;
   80|  2.63M|    } 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|  33.0k|      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|  33.0k|      const auto vH = (in & v_fc) == v_dc;
   93|  33.0k|      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|  33.0k|      const uint16_t L = static_cast<uint16_t>(~H & surrogates_bitmask);
   98|       |
   99|  33.0k|      const uint16_t a = static_cast<uint16_t>(
  100|  33.0k|          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|  33.0k|      const uint16_t b = static_cast<uint16_t>(
  104|  33.0k|          a << 1); // Just mark that the opinput - startite fact is hold,
  105|       |                   // thanks to that we have only two masks for valid case.
  106|  33.0k|      const uint16_t c = static_cast<uint16_t>(
  107|  33.0k|          V | a | b); // Combine all the masks into the final one.
  108|       |
  109|  33.0k|      if (c == 0xffff) {
  ------------------
  |  Branch (109:11): [True: 27.5k, False: 5.53k]
  ------------------
  110|       |        // The whole input register contains valid UTF-16, i.e.,
  111|       |        // either single code units or proper surrogate pairs.
  112|  27.5k|        input += 16;
  113|  27.5k|      } else if (c == 0x7fff) {
  ------------------
  |  Branch (113:18): [True: 5.27k, 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|  5.27k|        input += 15;
  119|  5.27k|      } else {
  120|    254|        return result(error_code::SURROGATE, input - start);
  121|    254|      }
  122|  33.0k|    }
  123|  2.67M|  }
  124|       |
  125|  2.32k|  return result(error_code::SUCCESS, input - start);
  126|  2.57k|}

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

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

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_128avx2_convert_latin1_to_utf32EPKcmPDi:
    3|    126|                             char32_t *utf32_output) {
    4|    126|  size_t rounded_len = ((len | 7) ^ 7); // Round down to nearest multiple of 8
    5|       |
    6|  1.36M|  for (size_t i = 0; i < rounded_len; i += 8) {
  ------------------
  |  Branch (6:22): [True: 1.36M, False: 126]
  ------------------
    7|       |    // Load 8 Latin1 characters into a 64-bit register
    8|  1.36M|    __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|  1.36M|    __m256i out = _mm256_cvtepu8_epi32(in);
   13|       |
   14|       |    // Store the results back to memory
   15|  1.36M|    _mm256_storeu_si256((__m256i *)&utf32_output[i], out);
   16|  1.36M|  }
   17|       |
   18|       |  // return pointers pointing to where we left off
   19|    126|  return std::make_pair(buf + rounded_len, utf32_output + rounded_len);
   20|    126|}

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

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

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_127avx2_convert_utf16_to_utf32ILNS_10endiannessE0EEENSt3__14pairIPKDsPDiEES7_mS8_:
   57|    414|                            char32_t *utf32_output) {
   58|    414|  const char16_t *end = buf + len;
   59|    414|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
   60|    414|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
   61|       |
   62|   173k|  while (end - buf >= 16) {
  ------------------
  |  Branch (62:10): [True: 173k, False: 316]
  ------------------
   63|   173k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
   64|   173k|    if (big_endian) {
  ------------------
  |  Branch (64:9): [Folded, False: 173k]
  ------------------
   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|   173k|    const __m256i surrogates_bytemask =
   75|   173k|        _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|   173k|    const uint32_t surrogates_bitmask =
   80|   173k|        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|   173k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (83:9): [True: 167k, False: 6.44k]
  ------------------
   84|       |      // case: we extend all sixteen 16-bit code units to sixteen 32-bit code
   85|       |      // units
   86|   167k|      _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output),
   87|   167k|                          _mm256_cvtepu16_epi32(_mm256_castsi256_si128(in)));
   88|   167k|      _mm256_storeu_si256(
   89|   167k|          reinterpret_cast<__m256i *>(utf32_output + 8),
   90|   167k|          _mm256_cvtepu16_epi32(_mm256_extractf128_si256(in, 1)));
   91|   167k|      utf32_output += 16;
   92|   167k|      buf += 16;
   93|       |      // surrogate pair(s) in a register
   94|   167k|    } 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.44k|      size_t forward = 15;
   99|  6.44k|      size_t k = 0;
  100|  6.44k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (100:11): [True: 0, False: 6.44k]
  ------------------
  101|      0|        forward = size_t(end - buf - 1);
  102|      0|      }
  103|  95.2k|      for (; k < forward; k++) {
  ------------------
  |  Branch (103:14): [True: 88.8k, False: 6.35k]
  ------------------
  104|  88.8k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  105|  88.8k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (105:13): [True: 81.4k, False: 7.39k]
  ------------------
  106|       |          // No surrogate pair
  107|  81.4k|          *utf32_output++ = char32_t(word);
  108|  81.4k|        } else {
  109|       |          // must be a surrogate pair
  110|  7.39k|          uint16_t diff = uint16_t(word - 0xD800);
  111|  7.39k|          uint16_t next_word =
  112|  7.39k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  113|  7.39k|          k++;
  114|  7.39k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  115|  7.39k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (115:15): [True: 98, False: 7.29k]
  ------------------
  116|     98|            return std::make_pair(nullptr, utf32_output);
  117|     98|          }
  118|  7.29k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  119|  7.29k|          *utf32_output++ = char32_t(value);
  120|  7.29k|        }
  121|  88.8k|      }
  122|  6.35k|      buf += k;
  123|  6.35k|    }
  124|   173k|  } // while
  125|    316|  return std::make_pair(buf, utf32_output);
  126|    414|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_127avx2_convert_utf16_to_utf32ILNS_10endiannessE1EEENSt3__14pairIPKDsPDiEES7_mS8_:
   57|    384|                            char32_t *utf32_output) {
   58|    384|  const char16_t *end = buf + len;
   59|    384|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
   60|    384|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
   61|       |
   62|   306k|  while (end - buf >= 16) {
  ------------------
  |  Branch (62:10): [True: 306k, False: 314]
  ------------------
   63|   306k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
   64|   306k|    if (big_endian) {
  ------------------
  |  Branch (64:9): [True: 306k, Folded]
  ------------------
   65|   306k|      const __m256i swap = _mm256_setr_epi8(
   66|   306k|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   67|   306k|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   68|   306k|      in = _mm256_shuffle_epi8(in, swap);
   69|   306k|    }
   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|   306k|    const __m256i surrogates_bytemask =
   75|   306k|        _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|   306k|    const uint32_t surrogates_bitmask =
   80|   306k|        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|   306k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (83:9): [True: 304k, False: 1.11k]
  ------------------
   84|       |      // case: we extend all sixteen 16-bit code units to sixteen 32-bit code
   85|       |      // units
   86|   304k|      _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output),
   87|   304k|                          _mm256_cvtepu16_epi32(_mm256_castsi256_si128(in)));
   88|   304k|      _mm256_storeu_si256(
   89|   304k|          reinterpret_cast<__m256i *>(utf32_output + 8),
   90|   304k|          _mm256_cvtepu16_epi32(_mm256_extractf128_si256(in, 1)));
   91|   304k|      utf32_output += 16;
   92|   304k|      buf += 16;
   93|       |      // surrogate pair(s) in a register
   94|   304k|    } 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|  1.11k|      size_t forward = 15;
   99|  1.11k|      size_t k = 0;
  100|  1.11k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (100:11): [True: 0, False: 1.11k]
  ------------------
  101|      0|        forward = size_t(end - buf - 1);
  102|      0|      }
  103|  15.2k|      for (; k < forward; k++) {
  ------------------
  |  Branch (103:14): [True: 14.2k, False: 1.04k]
  ------------------
  104|  14.2k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  105|  14.2k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (105:13): [True: 12.2k, False: 2.03k]
  ------------------
  106|       |          // No surrogate pair
  107|  12.2k|          *utf32_output++ = char32_t(word);
  108|  12.2k|        } else {
  109|       |          // must be a surrogate pair
  110|  2.03k|          uint16_t diff = uint16_t(word - 0xD800);
  111|  2.03k|          uint16_t next_word =
  112|  2.03k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  113|  2.03k|          k++;
  114|  2.03k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  115|  2.03k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (115:15): [True: 70, False: 1.96k]
  ------------------
  116|     70|            return std::make_pair(nullptr, utf32_output);
  117|     70|          }
  118|  1.96k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  119|  1.96k|          *utf32_output++ = char32_t(value);
  120|  1.96k|        }
  121|  14.2k|      }
  122|  1.04k|      buf += k;
  123|  1.04k|    }
  124|   306k|  } // while
  125|    314|  return std::make_pair(buf, utf32_output);
  126|    384|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_139avx2_convert_utf16_to_utf32_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPDiEEPKDsmS7_:
  138|    408|                                        char32_t *utf32_output) {
  139|    408|  const char16_t *start = buf;
  140|    408|  const char16_t *end = buf + len;
  141|    408|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
  142|    408|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
  143|       |
  144|   276k|  while (end - buf >= 16) {
  ------------------
  |  Branch (144:10): [True: 276k, False: 272]
  ------------------
  145|   276k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
  146|   276k|    if (big_endian) {
  ------------------
  |  Branch (146:9): [Folded, False: 276k]
  ------------------
  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|   276k|    const __m256i surrogates_bytemask =
  157|   276k|        _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|   276k|    const uint32_t surrogates_bitmask =
  162|   276k|        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|   276k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (165:9): [True: 266k, False: 9.87k]
  ------------------
  166|       |      // case: we extend all sixteen 16-bit code units to sixteen 32-bit code
  167|       |      // units
  168|   266k|      _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output),
  169|   266k|                          _mm256_cvtepu16_epi32(_mm256_castsi256_si128(in)));
  170|   266k|      _mm256_storeu_si256(
  171|   266k|          reinterpret_cast<__m256i *>(utf32_output + 8),
  172|   266k|          _mm256_cvtepu16_epi32(_mm256_extractf128_si256(in, 1)));
  173|   266k|      utf32_output += 16;
  174|   266k|      buf += 16;
  175|       |      // surrogate pair(s) in a register
  176|   266k|    } 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|  9.87k|      size_t forward = 15;
  181|  9.87k|      size_t k = 0;
  182|  9.87k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (182:11): [True: 0, False: 9.87k]
  ------------------
  183|      0|        forward = size_t(end - buf - 1);
  184|      0|      }
  185|   143k|      for (; k < forward; k++) {
  ------------------
  |  Branch (185:14): [True: 133k, False: 9.73k]
  ------------------
  186|   133k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  187|   133k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (187:13): [True: 119k, False: 14.3k]
  ------------------
  188|       |          // No surrogate pair
  189|   119k|          *utf32_output++ = char32_t(word);
  190|   119k|        } else {
  191|       |          // must be a surrogate pair
  192|  14.3k|          uint16_t diff = uint16_t(word - 0xD800);
  193|  14.3k|          uint16_t next_word =
  194|  14.3k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  195|  14.3k|          k++;
  196|  14.3k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  197|  14.3k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (197:15): [True: 136, False: 14.2k]
  ------------------
  198|    136|            return std::make_pair(
  199|    136|                result(error_code::SURROGATE, buf - start + k - 1),
  200|    136|                utf32_output);
  201|    136|          }
  202|  14.2k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  203|  14.2k|          *utf32_output++ = char32_t(value);
  204|  14.2k|        }
  205|   133k|      }
  206|  9.73k|      buf += k;
  207|  9.73k|    }
  208|   276k|  } // while
  209|    272|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf32_output);
  210|    408|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_139avx2_convert_utf16_to_utf32_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPDiEEPKDsmS7_:
  138|    362|                                        char32_t *utf32_output) {
  139|    362|  const char16_t *start = buf;
  140|    362|  const char16_t *end = buf + len;
  141|    362|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
  142|    362|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
  143|       |
  144|   206k|  while (end - buf >= 16) {
  ------------------
  |  Branch (144:10): [True: 205k, False: 290]
  ------------------
  145|   205k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
  146|   205k|    if (big_endian) {
  ------------------
  |  Branch (146:9): [True: 205k, Folded]
  ------------------
  147|   205k|      const __m256i swap = _mm256_setr_epi8(
  148|   205k|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
  149|   205k|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
  150|   205k|      in = _mm256_shuffle_epi8(in, swap);
  151|   205k|    }
  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|   205k|    const __m256i surrogates_bytemask =
  157|   205k|        _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|   205k|    const uint32_t surrogates_bitmask =
  162|   205k|        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|   205k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (165:9): [True: 203k, False: 1.77k]
  ------------------
  166|       |      // case: we extend all sixteen 16-bit code units to sixteen 32-bit code
  167|       |      // units
  168|   203k|      _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output),
  169|   203k|                          _mm256_cvtepu16_epi32(_mm256_castsi256_si128(in)));
  170|   203k|      _mm256_storeu_si256(
  171|   203k|          reinterpret_cast<__m256i *>(utf32_output + 8),
  172|   203k|          _mm256_cvtepu16_epi32(_mm256_extractf128_si256(in, 1)));
  173|   203k|      utf32_output += 16;
  174|   203k|      buf += 16;
  175|       |      // surrogate pair(s) in a register
  176|   203k|    } 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|  1.77k|      size_t forward = 15;
  181|  1.77k|      size_t k = 0;
  182|  1.77k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (182:11): [True: 0, False: 1.77k]
  ------------------
  183|      0|        forward = size_t(end - buf - 1);
  184|      0|      }
  185|  22.1k|      for (; k < forward; k++) {
  ------------------
  |  Branch (185:14): [True: 20.4k, False: 1.70k]
  ------------------
  186|  20.4k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  187|  20.4k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (187:13): [True: 14.1k, False: 6.30k]
  ------------------
  188|       |          // No surrogate pair
  189|  14.1k|          *utf32_output++ = char32_t(word);
  190|  14.1k|        } else {
  191|       |          // must be a surrogate pair
  192|  6.30k|          uint16_t diff = uint16_t(word - 0xD800);
  193|  6.30k|          uint16_t next_word =
  194|  6.30k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  195|  6.30k|          k++;
  196|  6.30k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  197|  6.30k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (197:15): [True: 72, False: 6.23k]
  ------------------
  198|     72|            return std::make_pair(
  199|     72|                result(error_code::SURROGATE, buf - start + k - 1),
  200|     72|                utf32_output);
  201|     72|          }
  202|  6.23k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  203|  6.23k|          *utf32_output++ = char32_t(value);
  204|  6.23k|        }
  205|  20.4k|      }
  206|  1.70k|      buf += k;
  207|  1.70k|    }
  208|   205k|  } // while
  209|    290|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf32_output);
  210|    362|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_126avx2_convert_utf16_to_utf8ILNS_10endiannessE0EEENSt3__14pairIPKDsPcEES7_mS8_:
   56|    838|avx2_convert_utf16_to_utf8(const char16_t *buf, size_t len, char *utf8_output) {
   57|    838|  const char16_t *end = buf + len;
   58|    838|  const __m256i v_0000 = _mm256_setzero_si256();
   59|    838|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
   60|    838|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
   61|    838|  const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
   62|    838|  const size_t safety_margin =
   63|    838|      12; // to avoid overruns, see issue
   64|       |          // https://github.com/simdutf/simdutf/issues/92
   65|       |
   66|   984k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (66:10): [True: 983k, False: 714]
  ------------------
   67|   983k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
   68|   983k|    if (big_endian) {
  ------------------
  |  Branch (68:9): [Folded, False: 983k]
  ------------------
   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|   983k|    const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
   76|   983k|    if (_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (76:9): [True: 755k, False: 227k]
  ------------------
   77|       |      // 1. pack the bytes
   78|   755k|      const __m128i utf8_packed = _mm_packus_epi16(
   79|   755k|          _mm256_castsi256_si128(in), _mm256_extractf128_si256(in, 1));
   80|       |      // 2. store (16 bytes)
   81|   755k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   82|       |      // 3. adjust pointers
   83|   755k|      buf += 16;
   84|   755k|      utf8_output += 16;
   85|   755k|      continue; // we are done for this round!
   86|   755k|    }
   87|       |    // no bits set above 7th bit
   88|   227k|    const __m256i one_byte_bytemask =
   89|   227k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
   90|   227k|    const uint32_t one_byte_bitmask =
   91|   227k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
   92|       |
   93|       |    // no bits set above 11th bit
   94|   227k|    const __m256i one_or_two_bytes_bytemask =
   95|   227k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000);
   96|   227k|    const uint32_t one_or_two_bytes_bitmask =
   97|   227k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
   98|   227k|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (98:9): [True: 128k, False: 99.1k]
  ------------------
   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|   128k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
  104|   128k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
  105|       |
  106|       |      // t0 = [000a|aaaa|bbbb|bb00]
  107|   128k|      const __m256i t0 = _mm256_slli_epi16(in, 2);
  108|       |      // t1 = [000a|aaaa|0000|0000]
  109|   128k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
  110|       |      // t2 = [0000|0000|00bb|bbbb]
  111|   128k|      const __m256i t2 = _mm256_and_si256(in, v_003f);
  112|       |      // t3 = [000a|aaaa|00bb|bbbb]
  113|   128k|      const __m256i t3 = _mm256_or_si256(t1, t2);
  114|       |      // t4 = [110a|aaaa|10bb|bbbb]
  115|   128k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
  116|       |
  117|       |      // 2. merge ASCII and 2-byte codewords
  118|   128k|      const __m256i utf8_unpacked =
  119|   128k|          _mm256_blendv_epi8(t4, in, one_byte_bytemask);
  120|       |
  121|       |      // 3. prepare bitmask for 8-bit lookup
  122|   128k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
  123|   128k|      const uint32_t M1 = M0 >> 7;
  124|   128k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
  125|       |      // 4. pack the bytes
  126|       |
  127|   128k|      const uint8_t *row =
  128|   128k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
  129|   128k|      const uint8_t *row_2 =
  130|   128k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
  131|   128k|                                                                       16)][0];
  132|       |
  133|   128k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  134|   128k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
  135|       |
  136|   128k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
  137|   128k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|   128k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|   128k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  138|       |      // 5. store bytes
  139|   128k|      _mm_storeu_si128((__m128i *)utf8_output,
  140|   128k|                       _mm256_castsi256_si128(utf8_packed));
  141|   128k|      utf8_output += row[0];
  142|   128k|      _mm_storeu_si128((__m128i *)utf8_output,
  143|   128k|                       _mm256_extractf128_si256(utf8_packed, 1));
  144|   128k|      utf8_output += row_2[0];
  145|       |
  146|       |      // 6. adjust pointers
  147|   128k|      buf += 16;
  148|   128k|      continue;
  149|   128k|    }
  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|  99.1k|    const __m256i surrogates_bytemask =
  154|  99.1k|        _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|  99.1k|    const uint32_t surrogates_bitmask =
  159|  99.1k|        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|  99.1k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (162:9): [True: 89.8k, False: 9.37k]
  ------------------
  163|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  164|  89.8k|      const __m256i dup_even = _mm256_setr_epi16(
  165|  89.8k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  166|  89.8k|          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|  89.8k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  196|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  197|  89.8k|      const __m256i t0 = _mm256_shuffle_epi8(in, dup_even);
  198|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  199|  89.8k|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  195|  89.8k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  200|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  201|  89.8k|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  195|  89.8k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  202|       |
  203|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  204|  89.8k|      const __m256i s0 = _mm256_srli_epi16(in, 4);
  205|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  206|  89.8k|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  195|  89.8k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  207|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  208|  89.8k|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  195|  89.8k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  209|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  210|  89.8k|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  195|  89.8k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  211|  89.8k|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  212|  89.8k|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  195|  89.8k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  213|  89.8k|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  214|  89.8k|#undef simdutf_vec
  215|       |
  216|       |      // 4. expand code units 16-bit => 32-bit
  217|  89.8k|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  218|  89.8k|      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|  89.8k|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  222|  89.8k|                            (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|  89.8k|      const uint8_t mask0 = uint8_t(mask);
  243|  89.8k|      const uint8_t *row0 =
  244|  89.8k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  245|  89.8k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  246|  89.8k|      const __m128i utf8_0 =
  247|  89.8k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  248|       |
  249|  89.8k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  250|  89.8k|      const uint8_t *row1 =
  251|  89.8k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  252|  89.8k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  253|  89.8k|      const __m128i utf8_1 =
  254|  89.8k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  255|       |
  256|  89.8k|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  257|  89.8k|      const uint8_t *row2 =
  258|  89.8k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  259|  89.8k|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  260|  89.8k|      const __m128i utf8_2 =
  261|  89.8k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  262|       |
  263|  89.8k|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  264|  89.8k|      const uint8_t *row3 =
  265|  89.8k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  266|  89.8k|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  267|  89.8k|      const __m128i utf8_3 =
  268|  89.8k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  269|       |
  270|  89.8k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  271|  89.8k|      utf8_output += row0[0];
  272|  89.8k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  273|  89.8k|      utf8_output += row1[0];
  274|  89.8k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  275|  89.8k|      utf8_output += row2[0];
  276|  89.8k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  277|  89.8k|      utf8_output += row3[0];
  278|  89.8k|      buf += 16;
  279|       |      // surrogate pair(s) in a register
  280|  89.8k|    } 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.37k|      size_t forward = 15;
  285|  9.37k|      size_t k = 0;
  286|  9.37k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (286:11): [True: 0, False: 9.37k]
  ------------------
  287|      0|        forward = size_t(end - buf - 1);
  288|      0|      }
  289|   138k|      for (; k < forward; k++) {
  ------------------
  |  Branch (289:14): [True: 129k, False: 9.24k]
  ------------------
  290|   129k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  291|   129k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (291:13): [True: 17.5k, False: 112k]
  ------------------
  292|  17.5k|          *utf8_output++ = char(word);
  293|   112k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (293:20): [True: 5.20k, False: 106k]
  ------------------
  294|  5.20k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  295|  5.20k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  296|   106k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (296:20): [True: 96.6k, False: 10.2k]
  ------------------
  297|  96.6k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  298|  96.6k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  299|  96.6k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  300|  96.6k|        } 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: 124, False: 10.1k]
  ------------------
  308|    124|            return std::make_pair(nullptr, utf8_output);
  309|    124|          }
  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|   129k|      }
  317|  9.24k|      buf += k;
  318|  9.24k|    }
  319|  99.1k|  } // while
  320|    714|  return std::make_pair(buf, utf8_output);
  321|    838|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_126avx2_convert_utf16_to_utf8ILNS_10endiannessE1EEENSt3__14pairIPKDsPcEES7_mS8_:
   56|    742|avx2_convert_utf16_to_utf8(const char16_t *buf, size_t len, char *utf8_output) {
   57|    742|  const char16_t *end = buf + len;
   58|    742|  const __m256i v_0000 = _mm256_setzero_si256();
   59|    742|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
   60|    742|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
   61|    742|  const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
   62|    742|  const size_t safety_margin =
   63|    742|      12; // to avoid overruns, see issue
   64|       |          // https://github.com/simdutf/simdutf/issues/92
   65|       |
   66|   850k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (66:10): [True: 849k, False: 648]
  ------------------
   67|   849k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
   68|   849k|    if (big_endian) {
  ------------------
  |  Branch (68:9): [True: 849k, Folded]
  ------------------
   69|   849k|      const __m256i swap = _mm256_setr_epi8(
   70|   849k|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   71|   849k|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   72|   849k|      in = _mm256_shuffle_epi8(in, swap);
   73|   849k|    }
   74|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
   75|   849k|    const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
   76|   849k|    if (_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (76:9): [True: 611k, False: 238k]
  ------------------
   77|       |      // 1. pack the bytes
   78|   611k|      const __m128i utf8_packed = _mm_packus_epi16(
   79|   611k|          _mm256_castsi256_si128(in), _mm256_extractf128_si256(in, 1));
   80|       |      // 2. store (16 bytes)
   81|   611k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   82|       |      // 3. adjust pointers
   83|   611k|      buf += 16;
   84|   611k|      utf8_output += 16;
   85|   611k|      continue; // we are done for this round!
   86|   611k|    }
   87|       |    // no bits set above 7th bit
   88|   238k|    const __m256i one_byte_bytemask =
   89|   238k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
   90|   238k|    const uint32_t one_byte_bitmask =
   91|   238k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
   92|       |
   93|       |    // no bits set above 11th bit
   94|   238k|    const __m256i one_or_two_bytes_bytemask =
   95|   238k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000);
   96|   238k|    const uint32_t one_or_two_bytes_bitmask =
   97|   238k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
   98|   238k|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (98:9): [True: 17.2k, False: 221k]
  ------------------
   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|  17.2k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
  104|  17.2k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
  105|       |
  106|       |      // t0 = [000a|aaaa|bbbb|bb00]
  107|  17.2k|      const __m256i t0 = _mm256_slli_epi16(in, 2);
  108|       |      // t1 = [000a|aaaa|0000|0000]
  109|  17.2k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
  110|       |      // t2 = [0000|0000|00bb|bbbb]
  111|  17.2k|      const __m256i t2 = _mm256_and_si256(in, v_003f);
  112|       |      // t3 = [000a|aaaa|00bb|bbbb]
  113|  17.2k|      const __m256i t3 = _mm256_or_si256(t1, t2);
  114|       |      // t4 = [110a|aaaa|10bb|bbbb]
  115|  17.2k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
  116|       |
  117|       |      // 2. merge ASCII and 2-byte codewords
  118|  17.2k|      const __m256i utf8_unpacked =
  119|  17.2k|          _mm256_blendv_epi8(t4, in, one_byte_bytemask);
  120|       |
  121|       |      // 3. prepare bitmask for 8-bit lookup
  122|  17.2k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
  123|  17.2k|      const uint32_t M1 = M0 >> 7;
  124|  17.2k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
  125|       |      // 4. pack the bytes
  126|       |
  127|  17.2k|      const uint8_t *row =
  128|  17.2k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
  129|  17.2k|      const uint8_t *row_2 =
  130|  17.2k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
  131|  17.2k|                                                                       16)][0];
  132|       |
  133|  17.2k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  134|  17.2k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
  135|       |
  136|  17.2k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
  137|  17.2k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|  17.2k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|  17.2k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  138|       |      // 5. store bytes
  139|  17.2k|      _mm_storeu_si128((__m128i *)utf8_output,
  140|  17.2k|                       _mm256_castsi256_si128(utf8_packed));
  141|  17.2k|      utf8_output += row[0];
  142|  17.2k|      _mm_storeu_si128((__m128i *)utf8_output,
  143|  17.2k|                       _mm256_extractf128_si256(utf8_packed, 1));
  144|  17.2k|      utf8_output += row_2[0];
  145|       |
  146|       |      // 6. adjust pointers
  147|  17.2k|      buf += 16;
  148|  17.2k|      continue;
  149|  17.2k|    }
  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|   221k|    const __m256i surrogates_bytemask =
  154|   221k|        _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|   221k|    const uint32_t surrogates_bitmask =
  159|   221k|        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|   221k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (162:9): [True: 216k, False: 4.76k]
  ------------------
  163|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  164|   216k|      const __m256i dup_even = _mm256_setr_epi16(
  165|   216k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  166|   216k|          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|   216k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  196|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  197|   216k|      const __m256i t0 = _mm256_shuffle_epi8(in, dup_even);
  198|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  199|   216k|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  195|   216k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  200|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  201|   216k|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  195|   216k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  202|       |
  203|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  204|   216k|      const __m256i s0 = _mm256_srli_epi16(in, 4);
  205|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  206|   216k|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  195|   216k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  207|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  208|   216k|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  195|   216k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  209|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  210|   216k|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  195|   216k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  211|   216k|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  212|   216k|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  195|   216k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  213|   216k|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  214|   216k|#undef simdutf_vec
  215|       |
  216|       |      // 4. expand code units 16-bit => 32-bit
  217|   216k|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  218|   216k|      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|   216k|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  222|   216k|                            (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|   216k|      const uint8_t mask0 = uint8_t(mask);
  243|   216k|      const uint8_t *row0 =
  244|   216k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  245|   216k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  246|   216k|      const __m128i utf8_0 =
  247|   216k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  248|       |
  249|   216k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  250|   216k|      const uint8_t *row1 =
  251|   216k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  252|   216k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  253|   216k|      const __m128i utf8_1 =
  254|   216k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  255|       |
  256|   216k|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  257|   216k|      const uint8_t *row2 =
  258|   216k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  259|   216k|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  260|   216k|      const __m128i utf8_2 =
  261|   216k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  262|       |
  263|   216k|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  264|   216k|      const uint8_t *row3 =
  265|   216k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  266|   216k|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  267|   216k|      const __m128i utf8_3 =
  268|   216k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  269|       |
  270|   216k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  271|   216k|      utf8_output += row0[0];
  272|   216k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  273|   216k|      utf8_output += row1[0];
  274|   216k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  275|   216k|      utf8_output += row2[0];
  276|   216k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  277|   216k|      utf8_output += row3[0];
  278|   216k|      buf += 16;
  279|       |      // surrogate pair(s) in a register
  280|   216k|    } 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|  4.76k|      size_t forward = 15;
  285|  4.76k|      size_t k = 0;
  286|  4.76k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (286:11): [True: 0, False: 4.76k]
  ------------------
  287|      0|        forward = size_t(end - buf - 1);
  288|      0|      }
  289|  70.2k|      for (; k < forward; k++) {
  ------------------
  |  Branch (289:14): [True: 65.6k, False: 4.67k]
  ------------------
  290|  65.6k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  291|  65.6k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (291:13): [True: 11.5k, False: 54.1k]
  ------------------
  292|  11.5k|          *utf8_output++ = char(word);
  293|  54.1k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (293:20): [True: 2.30k, False: 51.8k]
  ------------------
  294|  2.30k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  295|  2.30k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  296|  51.8k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (296:20): [True: 46.6k, False: 5.18k]
  ------------------
  297|  46.6k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  298|  46.6k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  299|  46.6k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  300|  46.6k|        } else {
  301|       |          // must be a surrogate pair
  302|  5.18k|          uint16_t diff = uint16_t(word - 0xD800);
  303|  5.18k|          uint16_t next_word =
  304|  5.18k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  305|  5.18k|          k++;
  306|  5.18k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  307|  5.18k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (307:15): [True: 94, False: 5.09k]
  ------------------
  308|     94|            return std::make_pair(nullptr, utf8_output);
  309|     94|          }
  310|  5.09k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  311|  5.09k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  312|  5.09k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  313|  5.09k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  314|  5.09k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  315|  5.09k|        }
  316|  65.6k|      }
  317|  4.67k|      buf += k;
  318|  4.67k|    }
  319|   221k|  } // while
  320|    648|  return std::make_pair(buf, utf8_output);
  321|    742|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_138avx2_convert_utf16_to_utf8_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
  333|    678|                                       char *utf8_output) {
  334|    678|  const char16_t *start = buf;
  335|    678|  const char16_t *end = buf + len;
  336|       |
  337|    678|  const __m256i v_0000 = _mm256_setzero_si256();
  338|    678|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
  339|    678|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
  340|    678|  const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
  341|    678|  const size_t safety_margin =
  342|    678|      12; // to avoid overruns, see issue
  343|       |          // https://github.com/simdutf/simdutf/issues/92
  344|       |
  345|   492k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (345:10): [True: 491k, False: 490]
  ------------------
  346|   491k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
  347|   491k|    if (big_endian) {
  ------------------
  |  Branch (347:9): [Folded, False: 491k]
  ------------------
  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|   491k|    const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
  355|   491k|    if (_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (355:9): [True: 265k, False: 226k]
  ------------------
  356|       |      // 1. pack the bytes
  357|   265k|      const __m128i utf8_packed = _mm_packus_epi16(
  358|   265k|          _mm256_castsi256_si128(in), _mm256_extractf128_si256(in, 1));
  359|       |      // 2. store (16 bytes)
  360|   265k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  361|       |      // 3. adjust pointers
  362|   265k|      buf += 16;
  363|   265k|      utf8_output += 16;
  364|   265k|      continue; // we are done for this round!
  365|   265k|    }
  366|       |    // no bits set above 7th bit
  367|   226k|    const __m256i one_byte_bytemask =
  368|   226k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
  369|   226k|    const uint32_t one_byte_bitmask =
  370|   226k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
  371|       |
  372|       |    // no bits set above 11th bit
  373|   226k|    const __m256i one_or_two_bytes_bytemask =
  374|   226k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000);
  375|   226k|    const uint32_t one_or_two_bytes_bitmask =
  376|   226k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
  377|   226k|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (377:9): [True: 91.6k, False: 134k]
  ------------------
  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|  91.6k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
  383|  91.6k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
  384|       |
  385|       |      // t0 = [000a|aaaa|bbbb|bb00]
  386|  91.6k|      const __m256i t0 = _mm256_slli_epi16(in, 2);
  387|       |      // t1 = [000a|aaaa|0000|0000]
  388|  91.6k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
  389|       |      // t2 = [0000|0000|00bb|bbbb]
  390|  91.6k|      const __m256i t2 = _mm256_and_si256(in, v_003f);
  391|       |      // t3 = [000a|aaaa|00bb|bbbb]
  392|  91.6k|      const __m256i t3 = _mm256_or_si256(t1, t2);
  393|       |      // t4 = [110a|aaaa|10bb|bbbb]
  394|  91.6k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
  395|       |
  396|       |      // 2. merge ASCII and 2-byte codewords
  397|  91.6k|      const __m256i utf8_unpacked =
  398|  91.6k|          _mm256_blendv_epi8(t4, in, one_byte_bytemask);
  399|       |
  400|       |      // 3. prepare bitmask for 8-bit lookup
  401|  91.6k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
  402|  91.6k|      const uint32_t M1 = M0 >> 7;
  403|  91.6k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
  404|       |      // 4. pack the bytes
  405|       |
  406|  91.6k|      const uint8_t *row =
  407|  91.6k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
  408|  91.6k|      const uint8_t *row_2 =
  409|  91.6k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
  410|  91.6k|                                                                       16)][0];
  411|       |
  412|  91.6k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  413|  91.6k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
  414|       |
  415|  91.6k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
  416|  91.6k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|  91.6k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|  91.6k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  417|       |      // 5. store bytes
  418|  91.6k|      _mm_storeu_si128((__m128i *)utf8_output,
  419|  91.6k|                       _mm256_castsi256_si128(utf8_packed));
  420|  91.6k|      utf8_output += row[0];
  421|  91.6k|      _mm_storeu_si128((__m128i *)utf8_output,
  422|  91.6k|                       _mm256_extractf128_si256(utf8_packed, 1));
  423|  91.6k|      utf8_output += row_2[0];
  424|       |
  425|       |      // 6. adjust pointers
  426|  91.6k|      buf += 16;
  427|  91.6k|      continue;
  428|  91.6k|    }
  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|   134k|    const __m256i surrogates_bytemask =
  433|   134k|        _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|   134k|    const uint32_t surrogates_bitmask =
  438|   134k|        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|   134k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (441:9): [True: 132k, False: 1.96k]
  ------------------
  442|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  443|   132k|      const __m256i dup_even = _mm256_setr_epi16(
  444|   132k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  445|   132k|          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|   132k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  475|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  476|   132k|      const __m256i t0 = _mm256_shuffle_epi8(in, dup_even);
  477|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  478|   132k|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  474|   132k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  479|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  480|   132k|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  474|   132k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  481|       |
  482|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  483|   132k|      const __m256i s0 = _mm256_srli_epi16(in, 4);
  484|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  485|   132k|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  474|   132k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  486|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  487|   132k|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  474|   132k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  488|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  489|   132k|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  474|   132k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  490|   132k|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  491|   132k|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  474|   132k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  492|   132k|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  493|   132k|#undef simdutf_vec
  494|       |
  495|       |      // 4. expand code units 16-bit => 32-bit
  496|   132k|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  497|   132k|      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|   132k|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  501|   132k|                            (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|   132k|      const uint8_t mask0 = uint8_t(mask);
  522|   132k|      const uint8_t *row0 =
  523|   132k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  524|   132k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  525|   132k|      const __m128i utf8_0 =
  526|   132k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  527|       |
  528|   132k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  529|   132k|      const uint8_t *row1 =
  530|   132k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  531|   132k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  532|   132k|      const __m128i utf8_1 =
  533|   132k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  534|       |
  535|   132k|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  536|   132k|      const uint8_t *row2 =
  537|   132k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  538|   132k|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  539|   132k|      const __m128i utf8_2 =
  540|   132k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  541|       |
  542|   132k|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  543|   132k|      const uint8_t *row3 =
  544|   132k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  545|   132k|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  546|   132k|      const __m128i utf8_3 =
  547|   132k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  548|       |
  549|   132k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  550|   132k|      utf8_output += row0[0];
  551|   132k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  552|   132k|      utf8_output += row1[0];
  553|   132k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  554|   132k|      utf8_output += row2[0];
  555|   132k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  556|   132k|      utf8_output += row3[0];
  557|   132k|      buf += 16;
  558|       |      // surrogate pair(s) in a register
  559|   132k|    } 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|  1.96k|      size_t forward = 15;
  564|  1.96k|      size_t k = 0;
  565|  1.96k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (565:11): [True: 0, False: 1.96k]
  ------------------
  566|      0|        forward = size_t(end - buf - 1);
  567|      0|      }
  568|  27.7k|      for (; k < forward; k++) {
  ------------------
  |  Branch (568:14): [True: 25.9k, False: 1.78k]
  ------------------
  569|  25.9k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  570|  25.9k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (570:13): [True: 5.57k, False: 20.4k]
  ------------------
  571|  5.57k|          *utf8_output++ = char(word);
  572|  20.4k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (572:20): [True: 1.52k, False: 18.8k]
  ------------------
  573|  1.52k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  574|  1.52k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  575|  18.8k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (575:20): [True: 16.6k, False: 2.28k]
  ------------------
  576|  16.6k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  577|  16.6k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  578|  16.6k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  579|  16.6k|        } else {
  580|       |          // must be a surrogate pair
  581|  2.28k|          uint16_t diff = uint16_t(word - 0xD800);
  582|  2.28k|          uint16_t next_word =
  583|  2.28k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  584|  2.28k|          k++;
  585|  2.28k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  586|  2.28k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (586:15): [True: 188, False: 2.09k]
  ------------------
  587|    188|            return std::make_pair(
  588|    188|                result(error_code::SURROGATE, buf - start + k - 1),
  589|    188|                utf8_output);
  590|    188|          }
  591|  2.09k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  592|  2.09k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  593|  2.09k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  594|  2.09k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  595|  2.09k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  596|  2.09k|        }
  597|  25.9k|      }
  598|  1.78k|      buf += k;
  599|  1.78k|    }
  600|   134k|  } // while
  601|    490|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  602|    678|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_138avx2_convert_utf16_to_utf8_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
  333|    716|                                       char *utf8_output) {
  334|    716|  const char16_t *start = buf;
  335|    716|  const char16_t *end = buf + len;
  336|       |
  337|    716|  const __m256i v_0000 = _mm256_setzero_si256();
  338|    716|  const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
  339|    716|  const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
  340|    716|  const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
  341|    716|  const size_t safety_margin =
  342|    716|      12; // to avoid overruns, see issue
  343|       |          // https://github.com/simdutf/simdutf/issues/92
  344|       |
  345|   789k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (345:10): [True: 789k, False: 516]
  ------------------
  346|   789k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
  347|   789k|    if (big_endian) {
  ------------------
  |  Branch (347:9): [True: 789k, Folded]
  ------------------
  348|   789k|      const __m256i swap = _mm256_setr_epi8(
  349|   789k|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
  350|   789k|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
  351|   789k|      in = _mm256_shuffle_epi8(in, swap);
  352|   789k|    }
  353|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
  354|   789k|    const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
  355|   789k|    if (_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (355:9): [True: 516k, False: 272k]
  ------------------
  356|       |      // 1. pack the bytes
  357|   516k|      const __m128i utf8_packed = _mm_packus_epi16(
  358|   516k|          _mm256_castsi256_si128(in), _mm256_extractf128_si256(in, 1));
  359|       |      // 2. store (16 bytes)
  360|   516k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  361|       |      // 3. adjust pointers
  362|   516k|      buf += 16;
  363|   516k|      utf8_output += 16;
  364|   516k|      continue; // we are done for this round!
  365|   516k|    }
  366|       |    // no bits set above 7th bit
  367|   272k|    const __m256i one_byte_bytemask =
  368|   272k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
  369|   272k|    const uint32_t one_byte_bitmask =
  370|   272k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
  371|       |
  372|       |    // no bits set above 11th bit
  373|   272k|    const __m256i one_or_two_bytes_bytemask =
  374|   272k|        _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000);
  375|   272k|    const uint32_t one_or_two_bytes_bitmask =
  376|   272k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
  377|   272k|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (377:9): [True: 67.2k, False: 205k]
  ------------------
  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|  67.2k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
  383|  67.2k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
  384|       |
  385|       |      // t0 = [000a|aaaa|bbbb|bb00]
  386|  67.2k|      const __m256i t0 = _mm256_slli_epi16(in, 2);
  387|       |      // t1 = [000a|aaaa|0000|0000]
  388|  67.2k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
  389|       |      // t2 = [0000|0000|00bb|bbbb]
  390|  67.2k|      const __m256i t2 = _mm256_and_si256(in, v_003f);
  391|       |      // t3 = [000a|aaaa|00bb|bbbb]
  392|  67.2k|      const __m256i t3 = _mm256_or_si256(t1, t2);
  393|       |      // t4 = [110a|aaaa|10bb|bbbb]
  394|  67.2k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
  395|       |
  396|       |      // 2. merge ASCII and 2-byte codewords
  397|  67.2k|      const __m256i utf8_unpacked =
  398|  67.2k|          _mm256_blendv_epi8(t4, in, one_byte_bytemask);
  399|       |
  400|       |      // 3. prepare bitmask for 8-bit lookup
  401|  67.2k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
  402|  67.2k|      const uint32_t M1 = M0 >> 7;
  403|  67.2k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
  404|       |      // 4. pack the bytes
  405|       |
  406|  67.2k|      const uint8_t *row =
  407|  67.2k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
  408|  67.2k|      const uint8_t *row_2 =
  409|  67.2k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
  410|  67.2k|                                                                       16)][0];
  411|       |
  412|  67.2k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  413|  67.2k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
  414|       |
  415|  67.2k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
  416|  67.2k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|  67.2k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|  67.2k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  417|       |      // 5. store bytes
  418|  67.2k|      _mm_storeu_si128((__m128i *)utf8_output,
  419|  67.2k|                       _mm256_castsi256_si128(utf8_packed));
  420|  67.2k|      utf8_output += row[0];
  421|  67.2k|      _mm_storeu_si128((__m128i *)utf8_output,
  422|  67.2k|                       _mm256_extractf128_si256(utf8_packed, 1));
  423|  67.2k|      utf8_output += row_2[0];
  424|       |
  425|       |      // 6. adjust pointers
  426|  67.2k|      buf += 16;
  427|  67.2k|      continue;
  428|  67.2k|    }
  429|       |    // 1. Check if there are any surrogate word in the input chunk.
  430|       |    //    We have also deal with situation when there is a surrogate word
  431|       |    //    at the end of a chunk.
  432|   205k|    const __m256i surrogates_bytemask =
  433|   205k|        _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|   205k|    const uint32_t surrogates_bitmask =
  438|   205k|        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|   205k|    if (surrogates_bitmask == 0x00000000) {
  ------------------
  |  Branch (441:9): [True: 180k, False: 25.5k]
  ------------------
  442|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  443|   180k|      const __m256i dup_even = _mm256_setr_epi16(
  444|   180k|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  445|   180k|          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|   180k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  475|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  476|   180k|      const __m256i t0 = _mm256_shuffle_epi8(in, dup_even);
  477|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  478|   180k|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  474|   180k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  479|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  480|   180k|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  474|   180k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  481|       |
  482|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  483|   180k|      const __m256i s0 = _mm256_srli_epi16(in, 4);
  484|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  485|   180k|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  474|   180k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  486|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  487|   180k|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  474|   180k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  488|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  489|   180k|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  474|   180k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  490|   180k|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  491|   180k|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  474|   180k|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  492|   180k|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  493|   180k|#undef simdutf_vec
  494|       |
  495|       |      // 4. expand code units 16-bit => 32-bit
  496|   180k|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  497|   180k|      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|   180k|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  501|   180k|                            (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|   180k|      const uint8_t mask0 = uint8_t(mask);
  522|   180k|      const uint8_t *row0 =
  523|   180k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  524|   180k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  525|   180k|      const __m128i utf8_0 =
  526|   180k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  527|       |
  528|   180k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  529|   180k|      const uint8_t *row1 =
  530|   180k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  531|   180k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  532|   180k|      const __m128i utf8_1 =
  533|   180k|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  534|       |
  535|   180k|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  536|   180k|      const uint8_t *row2 =
  537|   180k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  538|   180k|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  539|   180k|      const __m128i utf8_2 =
  540|   180k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  541|       |
  542|   180k|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  543|   180k|      const uint8_t *row3 =
  544|   180k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  545|   180k|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  546|   180k|      const __m128i utf8_3 =
  547|   180k|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  548|       |
  549|   180k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  550|   180k|      utf8_output += row0[0];
  551|   180k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  552|   180k|      utf8_output += row1[0];
  553|   180k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  554|   180k|      utf8_output += row2[0];
  555|   180k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  556|   180k|      utf8_output += row3[0];
  557|   180k|      buf += 16;
  558|       |      // surrogate pair(s) in a register
  559|   180k|    } 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|  25.5k|      size_t forward = 15;
  564|  25.5k|      size_t k = 0;
  565|  25.5k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (565:11): [True: 0, False: 25.5k]
  ------------------
  566|      0|        forward = size_t(end - buf - 1);
  567|      0|      }
  568|   381k|      for (; k < forward; k++) {
  ------------------
  |  Branch (568:14): [True: 356k, False: 25.3k]
  ------------------
  569|   356k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  570|   356k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (570:13): [True: 139k, False: 217k]
  ------------------
  571|   139k|          *utf8_output++ = char(word);
  572|   217k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (572:20): [True: 15.5k, False: 201k]
  ------------------
  573|  15.5k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  574|  15.5k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  575|   201k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (575:20): [True: 175k, False: 26.4k]
  ------------------
  576|   175k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  577|   175k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  578|   175k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  579|   175k|        } else {
  580|       |          // must be a surrogate pair
  581|  26.4k|          uint16_t diff = uint16_t(word - 0xD800);
  582|  26.4k|          uint16_t next_word =
  583|  26.4k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  584|  26.4k|          k++;
  585|  26.4k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  586|  26.4k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (586:15): [True: 200, False: 26.2k]
  ------------------
  587|    200|            return std::make_pair(
  588|    200|                result(error_code::SURROGATE, buf - start + k - 1),
  589|    200|                utf8_output);
  590|    200|          }
  591|  26.2k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  592|  26.2k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  593|  26.2k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  594|  26.2k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  595|  26.2k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  596|  26.2k|        }
  597|   356k|      }
  598|  25.3k|      buf += k;
  599|  25.3k|    }
  600|   205k|  } // while
  601|    516|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  602|    716|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_128avx2_convert_utf32_to_latin1EPKDimPc:
    3|    178|                             char *latin1_output) {
    4|    178|  const size_t rounded_len =
    5|    178|      len & ~0x1F; // Round down to nearest multiple of 32
    6|       |
    7|    178|  const __m256i high_bytes_mask = _mm256_set1_epi32(0xFFFFFF00);
    8|       |
    9|    734|  for (size_t i = 0; i < rounded_len; i += 4 * 8) {
  ------------------
  |  Branch (9:22): [True: 568, False: 166]
  ------------------
   10|    568|    __m256i a = _mm256_loadu_si256((__m256i *)(buf + 0 * 8));
   11|    568|    __m256i b = _mm256_loadu_si256((__m256i *)(buf + 1 * 8));
   12|    568|    __m256i c = _mm256_loadu_si256((__m256i *)(buf + 2 * 8));
   13|    568|    __m256i d = _mm256_loadu_si256((__m256i *)(buf + 3 * 8));
   14|       |
   15|    568|    const __m256i check_combined =
   16|    568|        _mm256_or_si256(_mm256_or_si256(a, b), _mm256_or_si256(c, d));
   17|       |
   18|    568|    if (!_mm256_testz_si256(check_combined, high_bytes_mask)) {
  ------------------
  |  Branch (18:9): [True: 12, False: 556]
  ------------------
   19|     12|      return std::make_pair(nullptr, latin1_output);
   20|     12|    }
   21|       |
   22|    556|    b = _mm256_slli_epi32(b, 1 * 8);
   23|    556|    c = _mm256_slli_epi32(c, 2 * 8);
   24|    556|    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|    556|    const __m256i t0 =
   35|    556|        _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|    556|    const __m256i shuffle_bytes =
   40|    556|        _mm256_setr_epi8(0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15,
   41|    556|                         0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15);
   42|       |
   43|    556|    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|    556|    const __m256i shuffle_dwords = _mm256_setr_epi32(0, 4, 1, 5, 2, 6, 3, 7);
   48|    556|    const __m256i t2 = _mm256_permutevar8x32_epi32(t1, shuffle_dwords);
   49|       |// clang format on
   50|       |
   51|    556|    _mm256_storeu_si256((__m256i *)latin1_output, t2);
   52|       |
   53|    556|    latin1_output += 32;
   54|    556|    buf += 32;
   55|    556|  }
   56|       |
   57|    166|  return std::make_pair(buf, latin1_output);
   58|    178|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_140avx2_convert_utf32_to_latin1_with_errorsEPKDimPc:
   62|    408|                                         char *latin1_output) {
   63|    408|  const size_t rounded_len =
   64|    408|      len & ~0x1F; // Round down to nearest multiple of 32
   65|       |
   66|    408|  const char32_t *start = buf;
   67|       |
   68|    408|  const __m256i high_bytes_mask = _mm256_set1_epi32(0xFFFFFF00);
   69|       |
   70|  14.3k|  for (size_t i = 0; i < rounded_len; i += 4 * 8) {
  ------------------
  |  Branch (70:22): [True: 14.0k, False: 302]
  ------------------
   71|  14.0k|    __m256i a = _mm256_loadu_si256((__m256i *)(buf + 0 * 8));
   72|  14.0k|    __m256i b = _mm256_loadu_si256((__m256i *)(buf + 1 * 8));
   73|  14.0k|    __m256i c = _mm256_loadu_si256((__m256i *)(buf + 2 * 8));
   74|  14.0k|    __m256i d = _mm256_loadu_si256((__m256i *)(buf + 3 * 8));
   75|       |
   76|  14.0k|    const __m256i check_combined =
   77|  14.0k|        _mm256_or_si256(_mm256_or_si256(a, b), _mm256_or_si256(c, d));
   78|       |
   79|  14.0k|    if (!_mm256_testz_si256(check_combined, high_bytes_mask)) {
  ------------------
  |  Branch (79:9): [True: 106, False: 13.9k]
  ------------------
   80|       |      // Fallback to scalar code for handling errors
   81|  1.15k|      for (int k = 0; k < 4 * 8; k++) {
  ------------------
  |  Branch (81:23): [True: 1.15k, False: 0]
  ------------------
   82|  1.15k|        char32_t codepoint = buf[k];
   83|  1.15k|        if (codepoint <= 0xFF) {
  ------------------
  |  Branch (83:13): [True: 1.05k, False: 106]
  ------------------
   84|  1.05k|          *latin1_output++ = static_cast<char>(codepoint);
   85|  1.05k|        } else {
   86|    106|          return std::make_pair(result(error_code::TOO_LARGE, buf - start + k),
   87|    106|                                latin1_output);
   88|    106|        }
   89|  1.15k|      }
   90|    106|    }
   91|       |
   92|  13.9k|    b = _mm256_slli_epi32(b, 1 * 8);
   93|  13.9k|    c = _mm256_slli_epi32(c, 2 * 8);
   94|  13.9k|    d = _mm256_slli_epi32(d, 3 * 8);
   95|       |
   96|  13.9k|    const __m256i t0 =
   97|  13.9k|        _mm256_or_si256(_mm256_or_si256(a, b), _mm256_or_si256(c, d));
   98|       |
   99|  13.9k|    const __m256i shuffle_bytes =
  100|  13.9k|        _mm256_setr_epi8(0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15,
  101|  13.9k|                         0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15);
  102|       |
  103|  13.9k|    const __m256i t1 = _mm256_shuffle_epi8(t0, shuffle_bytes);
  104|       |
  105|  13.9k|    const __m256i shuffle_dwords = _mm256_setr_epi32(0, 4, 1, 5, 2, 6, 3, 7);
  106|  13.9k|    const __m256i t2 = _mm256_permutevar8x32_epi32(t1, shuffle_dwords);
  107|       |
  108|  13.9k|    _mm256_storeu_si256((__m256i *)latin1_output, t2);
  109|       |
  110|  13.9k|    latin1_output += 32;
  111|  13.9k|    buf += 32;
  112|  13.9k|  }
  113|       |
  114|    302|  return std::make_pair(result(error_code::SUCCESS, buf - start),
  115|    302|                        latin1_output);
  116|    408|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_127avx2_convert_utf32_to_utf16ILNS_10endiannessE0EEENSt3__14pairIPKDiPDsEES7_mS8_:
    4|    738|                            char16_t *utf16_output) {
    5|    738|  const char32_t *end = buf + len;
    6|       |
    7|    738|  const size_t safety_margin =
    8|    738|      12; // to avoid overruns, see issue
    9|       |          // https://github.com/simdutf/simdutf/issues/92
   10|    738|  __m256i forbidden_bytemask = _mm256_setzero_si256();
   11|       |
   12|    738|  const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000);
   13|    738|  const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800);
   14|    738|  const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800);
   15|       |
   16|   152k|  while (end - buf >= std::ptrdiff_t(8 + safety_margin)) {
  ------------------
  |  Branch (16:10): [True: 151k, False: 604]
  ------------------
   17|   151k|    const __m256i in = _mm256_loadu_si256((__m256i *)buf);
   18|       |
   19|   151k|    if (simdutf_likely(_mm256_testz_si256(in, v_ffff0000))) {
  ------------------
  |  |   89|   151k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (89:31): [True: 148k, False: 2.70k]
  |  |  ------------------
  ------------------
   20|       |      // no bits set above 16th bit <=> can pack to UTF16
   21|       |      // without surrogate pairs
   22|   148k|      forbidden_bytemask = _mm256_or_si256(
   23|   148k|          forbidden_bytemask,
   24|   148k|          _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800));
   25|       |
   26|   148k|      __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),
   27|   148k|                                              _mm256_extractf128_si256(in, 1));
   28|   148k|      if (big_endian) {
  ------------------
  |  Branch (28:11): [Folded, False: 148k]
  ------------------
   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|   148k|      _mm_storeu_si128((__m128i *)utf16_output, utf16_packed);
   34|   148k|      utf16_output += 8;
   35|   148k|      buf += 8;
   36|   148k|    } else {
   37|  2.70k|      size_t forward = 7;
   38|  2.70k|      size_t k = 0;
   39|  2.70k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (39:11): [True: 0, False: 2.70k]
  ------------------
   40|      0|        forward = size_t(end - buf - 1);
   41|      0|      }
   42|  20.9k|      for (; k < forward; k++) {
  ------------------
  |  Branch (42:14): [True: 18.4k, False: 2.57k]
  ------------------
   43|  18.4k|        uint32_t word = buf[k];
   44|  18.4k|        if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (44:13): [True: 6.23k, False: 12.1k]
  ------------------
   45|       |          // will not generate a surrogate pair
   46|  6.23k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (46:15): [True: 822, False: 5.41k]
  |  Branch (46:33): [True: 26, False: 796]
  ------------------
   47|     26|            return std::make_pair(nullptr, utf16_output);
   48|     26|          }
   49|  6.20k|          *utf16_output++ =
   50|  6.20k|              big_endian
  ------------------
  |  Branch (50:15): [Folded, False: 6.20k]
  ------------------
   51|  6.20k|                  ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8))
   52|  6.20k|                  : char16_t(word);
   53|  12.1k|        } else {
   54|       |          // will generate a surrogate pair
   55|  12.1k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (55:15): [True: 108, False: 12.0k]
  ------------------
   56|    108|            return std::make_pair(nullptr, utf16_output);
   57|    108|          }
   58|  12.0k|          word -= 0x10000;
   59|  12.0k|          uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
   60|  12.0k|          uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
   61|  12.0k|          if (big_endian) {
  ------------------
  |  Branch (61:15): [Folded, False: 12.0k]
  ------------------
   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.0k|          *utf16_output++ = char16_t(high_surrogate);
   68|  12.0k|          *utf16_output++ = char16_t(low_surrogate);
   69|  12.0k|        }
   70|  18.4k|      }
   71|  2.57k|      buf += k;
   72|  2.57k|    }
   73|   151k|  }
   74|       |
   75|       |  // check for invalid input
   76|    604|  if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (76:7): [True: 46, False: 558]
  ------------------
   77|     46|    return std::make_pair(nullptr, utf16_output);
   78|     46|  }
   79|       |
   80|    558|  return std::make_pair(buf, utf16_output);
   81|    604|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_127avx2_convert_utf32_to_utf16ILNS_10endiannessE1EEENSt3__14pairIPKDiPDsEES7_mS8_:
    4|    634|                            char16_t *utf16_output) {
    5|    634|  const char32_t *end = buf + len;
    6|       |
    7|    634|  const size_t safety_margin =
    8|    634|      12; // to avoid overruns, see issue
    9|       |          // https://github.com/simdutf/simdutf/issues/92
   10|    634|  __m256i forbidden_bytemask = _mm256_setzero_si256();
   11|       |
   12|    634|  const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000);
   13|    634|  const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800);
   14|    634|  const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800);
   15|       |
   16|  26.6k|  while (end - buf >= std::ptrdiff_t(8 + safety_margin)) {
  ------------------
  |  Branch (16:10): [True: 26.1k, False: 452]
  ------------------
   17|  26.1k|    const __m256i in = _mm256_loadu_si256((__m256i *)buf);
   18|       |
   19|  26.1k|    if (simdutf_likely(_mm256_testz_si256(in, v_ffff0000))) {
  ------------------
  |  |   89|  26.1k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (89:31): [True: 23.3k, False: 2.83k]
  |  |  ------------------
  ------------------
   20|       |      // no bits set above 16th bit <=> can pack to UTF16
   21|       |      // without surrogate pairs
   22|  23.3k|      forbidden_bytemask = _mm256_or_si256(
   23|  23.3k|          forbidden_bytemask,
   24|  23.3k|          _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800));
   25|       |
   26|  23.3k|      __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),
   27|  23.3k|                                              _mm256_extractf128_si256(in, 1));
   28|  23.3k|      if (big_endian) {
  ------------------
  |  Branch (28:11): [True: 23.3k, Folded]
  ------------------
   29|  23.3k|        const __m128i swap =
   30|  23.3k|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   31|  23.3k|        utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
   32|  23.3k|      }
   33|  23.3k|      _mm_storeu_si128((__m128i *)utf16_output, utf16_packed);
   34|  23.3k|      utf16_output += 8;
   35|  23.3k|      buf += 8;
   36|  23.3k|    } else {
   37|  2.83k|      size_t forward = 7;
   38|  2.83k|      size_t k = 0;
   39|  2.83k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (39:11): [True: 0, False: 2.83k]
  ------------------
   40|      0|        forward = size_t(end - buf - 1);
   41|      0|      }
   42|  21.7k|      for (; k < forward; k++) {
  ------------------
  |  Branch (42:14): [True: 19.0k, False: 2.65k]
  ------------------
   43|  19.0k|        uint32_t word = buf[k];
   44|  19.0k|        if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (44:13): [True: 2.83k, False: 16.2k]
  ------------------
   45|       |          // will not generate a surrogate pair
   46|  2.83k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (46:15): [True: 564, False: 2.26k]
  |  Branch (46:33): [True: 22, False: 542]
  ------------------
   47|     22|            return std::make_pair(nullptr, utf16_output);
   48|     22|          }
   49|  2.81k|          *utf16_output++ =
   50|  2.81k|              big_endian
  ------------------
  |  Branch (50:15): [True: 2.81k, Folded]
  ------------------
   51|  2.81k|                  ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8))
   52|  2.81k|                  : char16_t(word);
   53|  16.2k|        } else {
   54|       |          // will generate a surrogate pair
   55|  16.2k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (55:15): [True: 160, False: 16.1k]
  ------------------
   56|    160|            return std::make_pair(nullptr, utf16_output);
   57|    160|          }
   58|  16.1k|          word -= 0x10000;
   59|  16.1k|          uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
   60|  16.1k|          uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
   61|  16.1k|          if (big_endian) {
  ------------------
  |  Branch (61:15): [True: 16.1k, Folded]
  ------------------
   62|  16.1k|            high_surrogate =
   63|  16.1k|                uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
   64|  16.1k|            low_surrogate =
   65|  16.1k|                uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
   66|  16.1k|          }
   67|  16.1k|          *utf16_output++ = char16_t(high_surrogate);
   68|  16.1k|          *utf16_output++ = char16_t(low_surrogate);
   69|  16.1k|        }
   70|  19.0k|      }
   71|  2.65k|      buf += k;
   72|  2.65k|    }
   73|  26.1k|  }
   74|       |
   75|       |  // check for invalid input
   76|    452|  if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (76:7): [True: 34, False: 418]
  ------------------
   77|     34|    return std::make_pair(nullptr, utf16_output);
   78|     34|  }
   79|       |
   80|    418|  return std::make_pair(buf, utf16_output);
   81|    452|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_139avx2_convert_utf32_to_utf16_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPDsEEPKDimS7_:
   86|    658|                                        char16_t *utf16_output) {
   87|    658|  const char32_t *start = buf;
   88|    658|  const char32_t *end = buf + len;
   89|       |
   90|    658|  const size_t safety_margin =
   91|    658|      12; // to avoid overruns, see issue
   92|       |          // https://github.com/simdutf/simdutf/issues/92
   93|       |
   94|    658|  const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000);
   95|    658|  const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800);
   96|    658|  const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800);
   97|       |
   98|  56.2k|  while (end - buf >= std::ptrdiff_t(8 + safety_margin)) {
  ------------------
  |  Branch (98:10): [True: 55.7k, False: 484]
  ------------------
   99|  55.7k|    const __m256i in = _mm256_loadu_si256((__m256i *)buf);
  100|       |
  101|  55.7k|    if (simdutf_likely(_mm256_testz_si256(in, v_ffff0000))) {
  ------------------
  |  |   89|  55.7k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (89:31): [True: 53.4k, False: 2.36k]
  |  |  ------------------
  ------------------
  102|       |      // no bits set above 16th bit <=> can pack to UTF16 without surrogate
  103|       |      // pairs
  104|  53.4k|      const __m256i forbidden_bytemask =
  105|  53.4k|          _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800);
  106|  53.4k|      if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) !=
  ------------------
  |  Branch (106:11): [True: 32, False: 53.3k]
  ------------------
  107|  53.4k|          0x0) {
  108|     32|        return std::make_pair(result(error_code::SURROGATE, buf - start),
  109|     32|                              utf16_output);
  110|     32|      }
  111|       |
  112|  53.3k|      __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),
  113|  53.3k|                                              _mm256_extractf128_si256(in, 1));
  114|  53.3k|      if (big_endian) {
  ------------------
  |  Branch (114:11): [Folded, False: 53.3k]
  ------------------
  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|  53.3k|      _mm_storeu_si128((__m128i *)utf16_output, utf16_packed);
  120|  53.3k|      utf16_output += 8;
  121|  53.3k|      buf += 8;
  122|  53.3k|    } else {
  123|  2.36k|      size_t forward = 7;
  124|  2.36k|      size_t k = 0;
  125|  2.36k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (125:11): [True: 0, False: 2.36k]
  ------------------
  126|      0|        forward = size_t(end - buf - 1);
  127|      0|      }
  128|  18.1k|      for (; k < forward; k++) {
  ------------------
  |  Branch (128:14): [True: 15.9k, False: 2.22k]
  ------------------
  129|  15.9k|        uint32_t word = buf[k];
  130|  15.9k|        if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (130:13): [True: 7.89k, False: 8.06k]
  ------------------
  131|       |          // will not generate a surrogate pair
  132|  7.89k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (132:15): [True: 714, False: 7.18k]
  |  Branch (132:33): [True: 6, False: 708]
  ------------------
  133|      6|            return std::make_pair(
  134|      6|                result(error_code::SURROGATE, buf - start + k), utf16_output);
  135|      6|          }
  136|  7.88k|          *utf16_output++ =
  137|  7.88k|              big_endian
  ------------------
  |  Branch (137:15): [Folded, False: 7.88k]
  ------------------
  138|  7.88k|                  ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8))
  139|  7.88k|                  : char16_t(word);
  140|  8.06k|        } else {
  141|       |          // will generate a surrogate pair
  142|  8.06k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (142:15): [True: 136, False: 7.92k]
  ------------------
  143|    136|            return std::make_pair(
  144|    136|                result(error_code::TOO_LARGE, buf - start + k), utf16_output);
  145|    136|          }
  146|  7.92k|          word -= 0x10000;
  147|  7.92k|          uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
  148|  7.92k|          uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
  149|  7.92k|          if (big_endian) {
  ------------------
  |  Branch (149:15): [Folded, False: 7.92k]
  ------------------
  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|  7.92k|          *utf16_output++ = char16_t(high_surrogate);
  156|  7.92k|          *utf16_output++ = char16_t(low_surrogate);
  157|  7.92k|        }
  158|  15.9k|      }
  159|  2.22k|      buf += k;
  160|  2.22k|    }
  161|  55.7k|  }
  162|       |
  163|    484|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf16_output);
  164|    658|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_139avx2_convert_utf32_to_utf16_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPDsEEPKDimS7_:
   86|    592|                                        char16_t *utf16_output) {
   87|    592|  const char32_t *start = buf;
   88|    592|  const char32_t *end = buf + len;
   89|       |
   90|    592|  const size_t safety_margin =
   91|    592|      12; // to avoid overruns, see issue
   92|       |          // https://github.com/simdutf/simdutf/issues/92
   93|       |
   94|    592|  const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000);
   95|    592|  const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800);
   96|    592|  const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800);
   97|       |
   98|  3.34k|  while (end - buf >= std::ptrdiff_t(8 + safety_margin)) {
  ------------------
  |  Branch (98:10): [True: 2.88k, False: 460]
  ------------------
   99|  2.88k|    const __m256i in = _mm256_loadu_si256((__m256i *)buf);
  100|       |
  101|  2.88k|    if (simdutf_likely(_mm256_testz_si256(in, v_ffff0000))) {
  ------------------
  |  |   89|  2.88k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (89:31): [True: 2.21k, False: 668]
  |  |  ------------------
  ------------------
  102|       |      // no bits set above 16th bit <=> can pack to UTF16 without surrogate
  103|       |      // pairs
  104|  2.21k|      const __m256i forbidden_bytemask =
  105|  2.21k|          _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800);
  106|  2.21k|      if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) !=
  ------------------
  |  Branch (106:11): [True: 8, False: 2.21k]
  ------------------
  107|  2.21k|          0x0) {
  108|      8|        return std::make_pair(result(error_code::SURROGATE, buf - start),
  109|      8|                              utf16_output);
  110|      8|      }
  111|       |
  112|  2.21k|      __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),
  113|  2.21k|                                              _mm256_extractf128_si256(in, 1));
  114|  2.21k|      if (big_endian) {
  ------------------
  |  Branch (114:11): [True: 2.21k, Folded]
  ------------------
  115|  2.21k|        const __m128i swap =
  116|  2.21k|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  117|  2.21k|        utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
  118|  2.21k|      }
  119|  2.21k|      _mm_storeu_si128((__m128i *)utf16_output, utf16_packed);
  120|  2.21k|      utf16_output += 8;
  121|  2.21k|      buf += 8;
  122|  2.21k|    } else {
  123|    668|      size_t forward = 7;
  124|    668|      size_t k = 0;
  125|    668|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (125:11): [True: 0, False: 668]
  ------------------
  126|      0|        forward = size_t(end - buf - 1);
  127|      0|      }
  128|  4.71k|      for (; k < forward; k++) {
  ------------------
  |  Branch (128:14): [True: 4.16k, False: 544]
  ------------------
  129|  4.16k|        uint32_t word = buf[k];
  130|  4.16k|        if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (130:13): [True: 2.21k, False: 1.95k]
  ------------------
  131|       |          // will not generate a surrogate pair
  132|  2.21k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (132:15): [True: 568, False: 1.65k]
  |  Branch (132:33): [True: 6, False: 562]
  ------------------
  133|      6|            return std::make_pair(
  134|      6|                result(error_code::SURROGATE, buf - start + k), utf16_output);
  135|      6|          }
  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|  1.95k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (142:15): [True: 118, False: 1.83k]
  ------------------
  143|    118|            return std::make_pair(
  144|    118|                result(error_code::TOO_LARGE, buf - start + k), utf16_output);
  145|    118|          }
  146|  1.83k|          word -= 0x10000;
  147|  1.83k|          uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
  148|  1.83k|          uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
  149|  1.83k|          if (big_endian) {
  ------------------
  |  Branch (149:15): [True: 1.83k, Folded]
  ------------------
  150|  1.83k|            high_surrogate =
  151|  1.83k|                uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
  152|  1.83k|            low_surrogate =
  153|  1.83k|                uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
  154|  1.83k|          }
  155|  1.83k|          *utf16_output++ = char16_t(high_surrogate);
  156|  1.83k|          *utf16_output++ = char16_t(low_surrogate);
  157|  1.83k|        }
  158|  4.16k|      }
  159|    544|      buf += k;
  160|    544|    }
  161|  2.88k|  }
  162|       |
  163|    460|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf16_output);
  164|    592|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_126avx2_convert_utf32_to_utf8EPKDimPc:
    2|    860|avx2_convert_utf32_to_utf8(const char32_t *buf, size_t len, char *utf8_output) {
    3|    860|  const char32_t *end = buf + len;
    4|    860|  const __m256i v_0000 = _mm256_setzero_si256();
    5|    860|  const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000);
    6|    860|  const __m256i v_ff80 = _mm256_set1_epi16((uint16_t)0xff80);
    7|    860|  const __m256i v_f800 = _mm256_set1_epi16((uint16_t)0xf800);
    8|    860|  const __m256i v_c080 = _mm256_set1_epi16((uint16_t)0xc080);
    9|    860|  const __m256i v_7fffffff = _mm256_set1_epi32((uint32_t)0x7fffffff);
   10|    860|  __m256i running_max = _mm256_setzero_si256();
   11|    860|  __m256i forbidden_bytemask = _mm256_setzero_si256();
   12|       |
   13|    860|  const size_t safety_margin =
   14|    860|      12; // to avoid overruns, see issue
   15|       |          // https://github.com/simdutf/simdutf/issues/92
   16|       |
   17|  34.7k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (17:10): [True: 34.0k, False: 676]
  ------------------
   18|  34.0k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
   19|  34.0k|    __m256i nextin = _mm256_loadu_si256((__m256i *)buf + 1);
   20|  34.0k|    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|  34.0k|    __m256i in_16 = _mm256_packus_epi32(_mm256_and_si256(in, v_7fffffff),
   25|  34.0k|                                        _mm256_and_si256(nextin, v_7fffffff));
   26|  34.0k|    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|  34.0k|    if (_mm256_testz_si256(in_16, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (31:9): [True: 30.3k, False: 3.72k]
  ------------------
   32|       |      // 1. pack the bytes
   33|  30.3k|      const __m128i utf8_packed = _mm_packus_epi16(
   34|  30.3k|          _mm256_castsi256_si128(in_16), _mm256_extractf128_si256(in_16, 1));
   35|       |      // 2. store (16 bytes)
   36|  30.3k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   37|       |      // 3. adjust pointers
   38|  30.3k|      buf += 16;
   39|  30.3k|      utf8_output += 16;
   40|  30.3k|      continue; // we are done for this round!
   41|  30.3k|    }
   42|       |    // no bits set above 7th bit
   43|  3.72k|    const __m256i one_byte_bytemask =
   44|  3.72k|        _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_ff80), v_0000);
   45|  3.72k|    const uint32_t one_byte_bitmask =
   46|  3.72k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
   47|       |
   48|       |    // no bits set above 11th bit
   49|  3.72k|    const __m256i one_or_two_bytes_bytemask =
   50|  3.72k|        _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_0000);
   51|  3.72k|    const uint32_t one_or_two_bytes_bitmask =
   52|  3.72k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
   53|  3.72k|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (53:9): [True: 1.45k, False: 2.27k]
  ------------------
   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|  1.45k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
   58|  1.45k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
   59|       |
   60|       |      // t0 = [000a|aaaa|bbbb|bb00]
   61|  1.45k|      const __m256i t0 = _mm256_slli_epi16(in_16, 2);
   62|       |      // t1 = [000a|aaaa|0000|0000]
   63|  1.45k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
   64|       |      // t2 = [0000|0000|00bb|bbbb]
   65|  1.45k|      const __m256i t2 = _mm256_and_si256(in_16, v_003f);
   66|       |      // t3 = [000a|aaaa|00bb|bbbb]
   67|  1.45k|      const __m256i t3 = _mm256_or_si256(t1, t2);
   68|       |      // t4 = [110a|aaaa|10bb|bbbb]
   69|  1.45k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
   70|       |
   71|       |      // 2. merge ASCII and 2-byte codewords
   72|  1.45k|      const __m256i utf8_unpacked =
   73|  1.45k|          _mm256_blendv_epi8(t4, in_16, one_byte_bytemask);
   74|       |
   75|       |      // 3. prepare bitmask for 8-bit lookup
   76|  1.45k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
   77|  1.45k|      const uint32_t M1 = M0 >> 7;
   78|  1.45k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
   79|       |      // 4. pack the bytes
   80|       |
   81|  1.45k|      const uint8_t *row =
   82|  1.45k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
   83|  1.45k|      const uint8_t *row_2 =
   84|  1.45k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
   85|  1.45k|                                                                       16)][0];
   86|       |
   87|  1.45k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
   88|  1.45k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
   89|       |
   90|  1.45k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
   91|  1.45k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|  1.45k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|  1.45k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
   92|       |      // 5. store bytes
   93|  1.45k|      _mm_storeu_si128((__m128i *)utf8_output,
   94|  1.45k|                       _mm256_castsi256_si128(utf8_packed));
   95|  1.45k|      utf8_output += row[0];
   96|  1.45k|      _mm_storeu_si128((__m128i *)utf8_output,
   97|  1.45k|                       _mm256_extractf128_si256(utf8_packed, 1));
   98|  1.45k|      utf8_output += row_2[0];
   99|       |
  100|       |      // 6. adjust pointers
  101|  1.45k|      buf += 16;
  102|  1.45k|      continue;
  103|  1.45k|    }
  104|       |    // Must check for overflow in packing
  105|  2.27k|    const __m256i saturation_bytemask = _mm256_cmpeq_epi32(
  106|  2.27k|        _mm256_and_si256(_mm256_or_si256(in, nextin), v_ffff0000), v_0000);
  107|  2.27k|    const uint32_t saturation_bitmask =
  108|  2.27k|        static_cast<uint32_t>(_mm256_movemask_epi8(saturation_bytemask));
  109|  2.27k|    if (saturation_bitmask == 0xffffffff) {
  ------------------
  |  Branch (109:9): [True: 850, False: 1.42k]
  ------------------
  110|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  111|    850|      const __m256i v_d800 = _mm256_set1_epi16((uint16_t)0xd800);
  112|    850|      forbidden_bytemask = _mm256_or_si256(
  113|    850|          forbidden_bytemask,
  114|    850|          _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_d800));
  115|       |
  116|    850|      const __m256i dup_even = _mm256_setr_epi16(
  117|    850|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  118|    850|          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|    850|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  148|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  149|    850|      const __m256i t0 = _mm256_shuffle_epi8(in_16, dup_even);
  150|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  151|    850|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  147|    850|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  152|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  153|    850|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  147|    850|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  154|       |
  155|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  156|    850|      const __m256i s0 = _mm256_srli_epi16(in_16, 4);
  157|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  158|    850|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  147|    850|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  159|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  160|    850|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  147|    850|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  161|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  162|    850|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  147|    850|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  163|    850|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  164|    850|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  147|    850|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  165|    850|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  166|    850|#undef simdutf_vec
  167|       |
  168|       |      // 4. expand code units 16-bit => 32-bit
  169|    850|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  170|    850|      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|    850|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  174|    850|                            (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|    850|      const uint8_t mask0 = uint8_t(mask);
  195|    850|      const uint8_t *row0 =
  196|    850|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  197|    850|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  198|    850|      const __m128i utf8_0 =
  199|    850|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  200|       |
  201|    850|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  202|    850|      const uint8_t *row1 =
  203|    850|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  204|    850|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  205|    850|      const __m128i utf8_1 =
  206|    850|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  207|       |
  208|    850|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  209|    850|      const uint8_t *row2 =
  210|    850|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  211|    850|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  212|    850|      const __m128i utf8_2 =
  213|    850|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  214|       |
  215|    850|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  216|    850|      const uint8_t *row3 =
  217|    850|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  218|    850|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  219|    850|      const __m128i utf8_3 =
  220|    850|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  221|       |
  222|    850|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  223|    850|      utf8_output += row0[0];
  224|    850|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  225|    850|      utf8_output += row1[0];
  226|    850|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  227|    850|      utf8_output += row2[0];
  228|    850|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  229|    850|      utf8_output += row3[0];
  230|    850|      buf += 16;
  231|  1.42k|    } 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.42k|      size_t forward = 15;
  237|  1.42k|      size_t k = 0;
  238|  1.42k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (238:11): [True: 0, False: 1.42k]
  ------------------
  239|      0|        forward = size_t(end - buf - 1);
  240|      0|      }
  241|  20.8k|      for (; k < forward; k++) {
  ------------------
  |  Branch (241:14): [True: 19.6k, False: 1.23k]
  ------------------
  242|  19.6k|        uint32_t word = buf[k];
  243|  19.6k|        if ((word & 0xFFFFFF80) == 0) { // 1-byte (ASCII)
  ------------------
  |  Branch (243:13): [True: 10.3k, False: 9.28k]
  ------------------
  244|  10.3k|          *utf8_output++ = char(word);
  245|  10.3k|        } else if ((word & 0xFFFFF800) == 0) { // 2-byte
  ------------------
  |  Branch (245:20): [True: 1.30k, False: 7.98k]
  ------------------
  246|  1.30k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  247|  1.30k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  248|  7.98k|        } else if ((word & 0xFFFF0000) == 0) { // 3-byte
  ------------------
  |  Branch (248:20): [True: 3.18k, False: 4.79k]
  ------------------
  249|  3.18k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (249:15): [True: 1.12k, False: 2.05k]
  |  Branch (249:33): [True: 30, False: 1.09k]
  ------------------
  250|     30|            return std::make_pair(nullptr, utf8_output);
  251|     30|          }
  252|  3.15k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  253|  3.15k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  254|  3.15k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  255|  4.79k|        } else { // 4-byte
  256|  4.79k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (256:15): [True: 154, False: 4.64k]
  ------------------
  257|    154|            return std::make_pair(nullptr, utf8_output);
  258|    154|          }
  259|  4.64k|          *utf8_output++ = char((word >> 18) | 0b11110000);
  260|  4.64k|          *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
  261|  4.64k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  262|  4.64k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  263|  4.64k|        }
  264|  19.6k|      }
  265|  1.23k|      buf += k;
  266|  1.23k|    }
  267|  2.27k|  } // while
  268|       |
  269|       |  // check for invalid input
  270|    676|  const __m256i v_10ffff = _mm256_set1_epi32((uint32_t)0x10ffff);
  271|    676|  if (static_cast<uint32_t>(_mm256_movemask_epi8(_mm256_cmpeq_epi32(
  ------------------
  |  Branch (271:7): [True: 26, False: 650]
  ------------------
  272|    676|          _mm256_max_epu32(running_max, v_10ffff), v_10ffff))) != 0xffffffff) {
  273|     26|    return std::make_pair(nullptr, utf8_output);
  274|     26|  }
  275|       |
  276|    650|  if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (276:7): [True: 66, False: 584]
  ------------------
  277|     66|    return std::make_pair(nullptr, utf8_output);
  278|     66|  }
  279|       |
  280|    584|  return std::make_pair(buf, utf8_output);
  281|    650|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_138avx2_convert_utf32_to_utf8_with_errorsEPKDimPc:
  285|    892|                                       char *utf8_output) {
  286|    892|  const char32_t *end = buf + len;
  287|    892|  const char32_t *start = buf;
  288|       |
  289|    892|  const __m256i v_0000 = _mm256_setzero_si256();
  290|    892|  const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000);
  291|    892|  const __m256i v_ff80 = _mm256_set1_epi16((uint16_t)0xff80);
  292|    892|  const __m256i v_f800 = _mm256_set1_epi16((uint16_t)0xf800);
  293|    892|  const __m256i v_c080 = _mm256_set1_epi16((uint16_t)0xc080);
  294|    892|  const __m256i v_7fffffff = _mm256_set1_epi32((uint32_t)0x7fffffff);
  295|    892|  const __m256i v_10ffff = _mm256_set1_epi32((uint32_t)0x10ffff);
  296|       |
  297|    892|  const size_t safety_margin =
  298|    892|      12; // to avoid overruns, see issue
  299|       |          // https://github.com/simdutf/simdutf/issues/92
  300|       |
  301|  33.6k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (301:10): [True: 33.0k, False: 644]
  ------------------
  302|  33.0k|    __m256i in = _mm256_loadu_si256((__m256i *)buf);
  303|  33.0k|    __m256i nextin = _mm256_loadu_si256((__m256i *)buf + 1);
  304|       |    // Check for too large input
  305|  33.0k|    const __m256i max_input =
  306|  33.0k|        _mm256_max_epu32(_mm256_max_epu32(in, nextin), v_10ffff);
  307|  33.0k|    if (static_cast<uint32_t>(_mm256_movemask_epi8(
  ------------------
  |  Branch (307:9): [True: 162, False: 32.8k]
  ------------------
  308|  33.0k|            _mm256_cmpeq_epi32(max_input, v_10ffff))) != 0xffffffff) {
  309|    162|      return std::make_pair(result(error_code::TOO_LARGE, buf - start),
  310|    162|                            utf8_output);
  311|    162|    }
  312|       |
  313|       |    // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned
  314|       |    // saturation
  315|  32.8k|    __m256i in_16 = _mm256_packus_epi32(_mm256_and_si256(in, v_7fffffff),
  316|  32.8k|                                        _mm256_and_si256(nextin, v_7fffffff));
  317|  32.8k|    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|  32.8k|    if (_mm256_testz_si256(in_16, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (322:9): [True: 27.7k, False: 5.16k]
  ------------------
  323|       |      // 1. pack the bytes
  324|  27.7k|      const __m128i utf8_packed = _mm_packus_epi16(
  325|  27.7k|          _mm256_castsi256_si128(in_16), _mm256_extractf128_si256(in_16, 1));
  326|       |      // 2. store (16 bytes)
  327|  27.7k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  328|       |      // 3. adjust pointers
  329|  27.7k|      buf += 16;
  330|  27.7k|      utf8_output += 16;
  331|  27.7k|      continue; // we are done for this round!
  332|  27.7k|    }
  333|       |    // no bits set above 7th bit
  334|  5.16k|    const __m256i one_byte_bytemask =
  335|  5.16k|        _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_ff80), v_0000);
  336|  5.16k|    const uint32_t one_byte_bitmask =
  337|  5.16k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
  338|       |
  339|       |    // no bits set above 11th bit
  340|  5.16k|    const __m256i one_or_two_bytes_bytemask =
  341|  5.16k|        _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_0000);
  342|  5.16k|    const uint32_t one_or_two_bytes_bitmask =
  343|  5.16k|        static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
  344|  5.16k|    if (one_or_two_bytes_bitmask == 0xffffffff) {
  ------------------
  |  Branch (344:9): [True: 2.65k, False: 2.50k]
  ------------------
  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.65k|      const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
  349|  2.65k|      const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
  350|       |
  351|       |      // t0 = [000a|aaaa|bbbb|bb00]
  352|  2.65k|      const __m256i t0 = _mm256_slli_epi16(in_16, 2);
  353|       |      // t1 = [000a|aaaa|0000|0000]
  354|  2.65k|      const __m256i t1 = _mm256_and_si256(t0, v_1f00);
  355|       |      // t2 = [0000|0000|00bb|bbbb]
  356|  2.65k|      const __m256i t2 = _mm256_and_si256(in_16, v_003f);
  357|       |      // t3 = [000a|aaaa|00bb|bbbb]
  358|  2.65k|      const __m256i t3 = _mm256_or_si256(t1, t2);
  359|       |      // t4 = [110a|aaaa|10bb|bbbb]
  360|  2.65k|      const __m256i t4 = _mm256_or_si256(t3, v_c080);
  361|       |
  362|       |      // 2. merge ASCII and 2-byte codewords
  363|  2.65k|      const __m256i utf8_unpacked =
  364|  2.65k|          _mm256_blendv_epi8(t4, in_16, one_byte_bytemask);
  365|       |
  366|       |      // 3. prepare bitmask for 8-bit lookup
  367|  2.65k|      const uint32_t M0 = one_byte_bitmask & 0x55555555;
  368|  2.65k|      const uint32_t M1 = M0 >> 7;
  369|  2.65k|      const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
  370|       |      // 4. pack the bytes
  371|       |
  372|  2.65k|      const uint8_t *row =
  373|  2.65k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
  374|  2.65k|      const uint8_t *row_2 =
  375|  2.65k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >>
  376|  2.65k|                                                                       16)][0];
  377|       |
  378|  2.65k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  379|  2.65k|      const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
  380|       |
  381|  2.65k|      const __m256i utf8_packed = _mm256_shuffle_epi8(
  382|  2.65k|          utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
  ------------------
  |  |    7|  2.65k|      _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1),                  \
  |  |    8|  2.65k|                                _mm256_castsi128_si256(xmm2), 2)
  ------------------
  383|       |      // 5. store bytes
  384|  2.65k|      _mm_storeu_si128((__m128i *)utf8_output,
  385|  2.65k|                       _mm256_castsi256_si128(utf8_packed));
  386|  2.65k|      utf8_output += row[0];
  387|  2.65k|      _mm_storeu_si128((__m128i *)utf8_output,
  388|  2.65k|                       _mm256_extractf128_si256(utf8_packed, 1));
  389|  2.65k|      utf8_output += row_2[0];
  390|       |
  391|       |      // 6. adjust pointers
  392|  2.65k|      buf += 16;
  393|  2.65k|      continue;
  394|  2.65k|    }
  395|       |    // Must check for overflow in packing
  396|  2.50k|    const __m256i saturation_bytemask = _mm256_cmpeq_epi32(
  397|  2.50k|        _mm256_and_si256(_mm256_or_si256(in, nextin), v_ffff0000), v_0000);
  398|  2.50k|    const uint32_t saturation_bitmask =
  399|  2.50k|        static_cast<uint32_t>(_mm256_movemask_epi8(saturation_bytemask));
  400|  2.50k|    if (saturation_bitmask == 0xffffffff) {
  ------------------
  |  Branch (400:9): [True: 908, False: 1.59k]
  ------------------
  401|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  402|       |
  403|       |      // Check for illegal surrogate code units
  404|    908|      const __m256i v_d800 = _mm256_set1_epi16((uint16_t)0xd800);
  405|    908|      const __m256i forbidden_bytemask =
  406|    908|          _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_d800);
  407|    908|      if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) !=
  ------------------
  |  Branch (407:11): [True: 52, False: 856]
  ------------------
  408|    908|          0x0) {
  409|     52|        return std::make_pair(result(error_code::SURROGATE, buf - start),
  410|     52|                              utf8_output);
  411|     52|      }
  412|       |
  413|    856|      const __m256i dup_even = _mm256_setr_epi16(
  414|    856|          0x0000, 0x0202, 0x0404, 0x0606, 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
  415|    856|          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|    856|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  445|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  446|    856|      const __m256i t0 = _mm256_shuffle_epi8(in_16, dup_even);
  447|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  448|    856|      const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  444|    856|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  449|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  450|    856|      const __m256i t2 = _mm256_or_si256(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  444|    856|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  451|       |
  452|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  453|    856|      const __m256i s0 = _mm256_srli_epi16(in_16, 4);
  454|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  455|    856|      const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  444|    856|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  456|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  457|    856|      const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  444|    856|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  458|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  459|    856|      const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  444|    856|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  460|    856|      const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask,
  461|    856|                                             simdutf_vec(0b0100000000000000));
  ------------------
  |  |  444|    856|#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  462|    856|      const __m256i s4 = _mm256_xor_si256(s3, m0);
  463|    856|#undef simdutf_vec
  464|       |
  465|       |      // 4. expand code units 16-bit => 32-bit
  466|    856|      const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
  467|    856|      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|    856|      const uint32_t mask = (one_byte_bitmask & 0x55555555) |
  471|    856|                            (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|    856|      const uint8_t mask0 = uint8_t(mask);
  492|    856|      const uint8_t *row0 =
  493|    856|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  494|    856|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  495|    856|      const __m128i utf8_0 =
  496|    856|          _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
  497|       |
  498|    856|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  499|    856|      const uint8_t *row1 =
  500|    856|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  501|    856|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  502|    856|      const __m128i utf8_1 =
  503|    856|          _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
  504|       |
  505|    856|      const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
  506|    856|      const uint8_t *row2 =
  507|    856|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
  508|    856|      const __m128i shuffle2 = _mm_loadu_si128((__m128i *)(row2 + 1));
  509|    856|      const __m128i utf8_2 =
  510|    856|          _mm_shuffle_epi8(_mm256_extractf128_si256(out0, 1), shuffle2);
  511|       |
  512|    856|      const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
  513|    856|      const uint8_t *row3 =
  514|    856|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
  515|    856|      const __m128i shuffle3 = _mm_loadu_si128((__m128i *)(row3 + 1));
  516|    856|      const __m128i utf8_3 =
  517|    856|          _mm_shuffle_epi8(_mm256_extractf128_si256(out1, 1), shuffle3);
  518|       |
  519|    856|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  520|    856|      utf8_output += row0[0];
  521|    856|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  522|    856|      utf8_output += row1[0];
  523|    856|      _mm_storeu_si128((__m128i *)utf8_output, utf8_2);
  524|    856|      utf8_output += row2[0];
  525|    856|      _mm_storeu_si128((__m128i *)utf8_output, utf8_3);
  526|    856|      utf8_output += row3[0];
  527|    856|      buf += 16;
  528|  1.59k|    } 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.59k|      size_t forward = 15;
  534|  1.59k|      size_t k = 0;
  535|  1.59k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (535:11): [True: 0, False: 1.59k]
  ------------------
  536|      0|        forward = size_t(end - buf - 1);
  537|      0|      }
  538|  25.2k|      for (; k < forward; k++) {
  ------------------
  |  Branch (538:14): [True: 23.6k, False: 1.56k]
  ------------------
  539|  23.6k|        uint32_t word = buf[k];
  540|  23.6k|        if ((word & 0xFFFFFF80) == 0) { // 1-byte (ASCII)
  ------------------
  |  Branch (540:13): [True: 9.53k, False: 14.1k]
  ------------------
  541|  9.53k|          *utf8_output++ = char(word);
  542|  14.1k|        } else if ((word & 0xFFFFF800) == 0) { // 2-byte
  ------------------
  |  Branch (542:20): [True: 1.97k, False: 12.1k]
  ------------------
  543|  1.97k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  544|  1.97k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  545|  12.1k|        } else if ((word & 0xFFFF0000) == 0) { // 3-byte
  ------------------
  |  Branch (545:20): [True: 7.14k, False: 5.05k]
  ------------------
  546|  7.14k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (546:15): [True: 4.04k, False: 3.09k]
  |  Branch (546:33): [True: 34, False: 4.01k]
  ------------------
  547|     34|            return std::make_pair(
  548|     34|                result(error_code::SURROGATE, buf - start + k), utf8_output);
  549|     34|          }
  550|  7.10k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  551|  7.10k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  552|  7.10k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  553|  7.10k|        } else { // 4-byte
  554|  5.05k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (554:15): [True: 0, False: 5.05k]
  ------------------
  555|      0|            return std::make_pair(
  556|      0|                result(error_code::TOO_LARGE, buf - start + k), utf8_output);
  557|      0|          }
  558|  5.05k|          *utf8_output++ = char((word >> 18) | 0b11110000);
  559|  5.05k|          *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
  560|  5.05k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  561|  5.05k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  562|  5.05k|        }
  563|  23.6k|      }
  564|  1.56k|      buf += k;
  565|  1.56k|    }
  566|  2.50k|  } // while
  567|       |
  568|    644|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  569|    892|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_129convert_masked_utf8_to_latin1EPKcmRPc:
    9|   613k|                                     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|   613k|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   21|       |
   22|   613k|  const uint16_t input_utf8_end_of_code_point_mask =
   23|   613k|      utf8_end_of_code_point_mask &
   24|   613k|      0xfff; // we are only processing 12 bytes in case it is not all ASCII
   25|       |
   26|   613k|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (26:7): [True: 3.33k, False: 610k]
  ------------------
   27|       |    // We process the data in chunks of 12 bytes.
   28|  3.33k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(latin1_output), in);
   29|  3.33k|    latin1_output += 12; // We wrote 12 characters.
   30|  3.33k|    return 12;           // We consumed 1 bytes.
   31|  3.33k|  }
   32|       |  /// We do not have a fast path available, so we fallback.
   33|   610k|  const uint8_t idx =
   34|   610k|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
   35|   610k|  const uint8_t consumed =
   36|   610k|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
   37|       |  // this indicates an invalid input:
   38|   610k|  if (idx >= 64) {
  ------------------
  |  Branch (38:7): [True: 33.5k, False: 577k]
  ------------------
   39|  33.5k|    return consumed;
   40|  33.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|   577k|  const __m128i sh =
   48|   577k|      _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
   49|   577k|  const __m128i perm = _mm_shuffle_epi8(in, sh);
   50|   577k|  const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   51|   577k|  const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   52|   577k|  __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   53|   577k|  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|   577k|  _mm_storel_epi64((__m128i *)latin1_output, latin1_packed);
   58|   577k|  latin1_output += 6; // We wrote 6 bytes.
   59|   577k|  return consumed;
   60|   610k|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_128convert_masked_utf8_to_utf16ILNS_10endiannessE0EEEmPKcmRPDs:
   10|  3.03M|                                    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|  3.03M|  const __m128i swap =
   23|  3.03M|      _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   24|  3.03M|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   25|  3.03M|  const uint16_t input_utf8_end_of_code_point_mask =
   26|  3.03M|      utf8_end_of_code_point_mask & 0xfff;
   27|  3.03M|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (27:7): [True: 10.9k, False: 3.02M]
  ------------------
   28|       |    // We process the data in chunks of 12 bytes.
   29|  10.9k|    __m256i ascii = _mm256_cvtepu8_epi16(in);
   30|  10.9k|    if (big_endian) {
  ------------------
  |  Branch (30:9): [Folded, False: 10.9k]
  ------------------
   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|  10.9k|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf16_output), ascii);
   37|  10.9k|    utf16_output += 12; // We wrote 12 16-bit characters.
   38|  10.9k|    return 12;          // We consumed 12 bytes.
   39|  10.9k|  }
   40|  3.02M|  if (((utf8_end_of_code_point_mask & 0xffff) == 0xaaaa)) {
  ------------------
  |  Branch (40:7): [True: 21.1k, False: 3.00M]
  ------------------
   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|  21.1k|    const __m128i sh =
   45|  21.1k|        _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   46|  21.1k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   47|  21.1k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   48|  21.1k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   49|  21.1k|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   50|  21.1k|    if (big_endian)
  ------------------
  |  Branch (50:9): [Folded, False: 21.1k]
  ------------------
   51|      0|      composed = _mm_shuffle_epi8(composed, swap);
   52|  21.1k|    _mm_storeu_si128((__m128i *)utf16_output, composed);
   53|  21.1k|    utf16_output += 8; // We wrote 16 bytes, 8 code points.
   54|  21.1k|    return 16;
   55|  21.1k|  }
   56|  3.00M|  if (input_utf8_end_of_code_point_mask == 0x924) {
  ------------------
  |  Branch (56:7): [True: 2.68k, False: 3.00M]
  ------------------
   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|  2.68k|    const __m128i sh =
   61|  2.68k|        _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
   62|  2.68k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   63|  2.68k|    const __m128i ascii =
   64|  2.68k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
   65|  2.68k|    const __m128i middlebyte =
   66|  2.68k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
   67|  2.68k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
   68|  2.68k|    const __m128i highbyte =
   69|  2.68k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
   70|  2.68k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
   71|  2.68k|    const __m128i composed =
   72|  2.68k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
   73|  2.68k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
   74|  2.68k|    if (big_endian)
  ------------------
  |  Branch (74:9): [Folded, False: 2.68k]
  ------------------
   75|      0|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
   76|  2.68k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
   77|  2.68k|    utf16_output += 4;
   78|  2.68k|    return 12;
   79|  2.68k|  }
   80|       |
   81|  3.00M|  const uint8_t idx = simdutf::tables::utf8_to_utf16::utf8bigindex
   82|  3.00M|      [input_utf8_end_of_code_point_mask][0];
   83|  3.00M|  const uint8_t consumed = simdutf::tables::utf8_to_utf16::utf8bigindex
   84|  3.00M|      [input_utf8_end_of_code_point_mask][1];
   85|  3.00M|  if (idx < 64) {
  ------------------
  |  Branch (85:7): [True: 2.57M, False: 427k]
  ------------------
   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.57M|    const __m128i sh = _mm_loadu_si128(
   93|  2.57M|        (const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
   94|  2.57M|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   95|  2.57M|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   96|  2.57M|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   97|  2.57M|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   98|  2.57M|    if (big_endian)
  ------------------
  |  Branch (98:9): [Folded, False: 2.57M]
  ------------------
   99|      0|      composed = _mm_shuffle_epi8(composed, swap);
  100|  2.57M|    _mm_storeu_si128((__m128i *)utf16_output, composed);
  101|  2.57M|    utf16_output += 6; // We wrote 12 bytes, 6 code points. There is a potential
  102|       |                       // overflow of 4 bytes.
  103|  2.57M|  } else if (idx < 145) {
  ------------------
  |  Branch (103:14): [True: 346k, False: 81.3k]
  ------------------
  104|       |    // FOUR (4) input code-code units
  105|   346k|    const __m128i sh = _mm_loadu_si128(
  106|   346k|        (const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
  107|   346k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  108|   346k|    const __m128i ascii =
  109|   346k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
  110|   346k|    const __m128i middlebyte =
  111|   346k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
  112|   346k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  113|   346k|    const __m128i highbyte =
  114|   346k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
  115|   346k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
  116|   346k|    const __m128i composed =
  117|   346k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
  118|   346k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
  119|   346k|    if (big_endian)
  ------------------
  |  Branch (119:9): [Folded, False: 346k]
  ------------------
  120|      0|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
  121|   346k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
  122|   346k|    utf16_output += 4; // Here we overflow by 8 bytes.
  123|   346k|  } else if (idx < 209) {
  ------------------
  |  Branch (123:14): [True: 80.5k, False: 808]
  ------------------
  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|  80.5k|    const __m128i sh = _mm_loadu_si128(
  135|  80.5k|        (const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
  136|  80.5k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  137|  80.5k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
  138|  80.5k|    const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
  139|  80.5k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  140|  80.5k|    __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
  141|       |    // correct for spurious high bit
  142|  80.5k|    const __m128i correct =
  143|  80.5k|        _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
  144|  80.5k|    middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
  145|  80.5k|    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|  80.5k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0xff000000));
  149|  80.5k|    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|  80.5k|    const __m128i composed =
  155|  80.5k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
  156|  80.5k|                     _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
  157|  80.5k|    const __m128i composedminus =
  158|  80.5k|        _mm_sub_epi32(composed, _mm_set1_epi32(0x10000));
  159|  80.5k|    const __m128i lowtenbits =
  160|  80.5k|        _mm_and_si128(composedminus, _mm_set1_epi32(0x3ff));
  161|       |    // Notice the 0x3ff mask:
  162|  80.5k|    const __m128i hightenbits =
  163|  80.5k|        _mm_and_si128(_mm_srli_epi32(composedminus, 10), _mm_set1_epi32(0x3ff));
  164|  80.5k|    const __m128i lowtenbitsadd =
  165|  80.5k|        _mm_add_epi32(lowtenbits, _mm_set1_epi32(0xDC00));
  166|  80.5k|    const __m128i hightenbitsadd =
  167|  80.5k|        _mm_add_epi32(hightenbits, _mm_set1_epi32(0xD800));
  168|  80.5k|    const __m128i lowtenbitsaddshifted = _mm_slli_epi32(lowtenbitsadd, 16);
  169|  80.5k|    __m128i surrogates = _mm_or_si128(hightenbitsadd, lowtenbitsaddshifted);
  170|  80.5k|    uint32_t basic_buffer[4];
  171|  80.5k|    uint32_t basic_buffer_swap[4];
  172|  80.5k|    if (big_endian) {
  ------------------
  |  Branch (172:9): [Folded, False: 80.5k]
  ------------------
  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|  80.5k|    _mm_storeu_si128((__m128i *)basic_buffer, composed);
  178|  80.5k|    uint32_t surrogate_buffer[4];
  179|  80.5k|    _mm_storeu_si128((__m128i *)surrogate_buffer, surrogates);
  180|   322k|    for (size_t i = 0; i < 3; i++) {
  ------------------
  |  Branch (180:24): [True: 241k, False: 80.5k]
  ------------------
  181|   241k|      if (basic_buffer[i] > 0x3c00000) {
  ------------------
  |  Branch (181:11): [True: 68.2k, False: 173k]
  ------------------
  182|  68.2k|        utf16_output[0] = uint16_t(surrogate_buffer[i] & 0xffff);
  183|  68.2k|        utf16_output[1] = uint16_t(surrogate_buffer[i] >> 16);
  184|  68.2k|        utf16_output += 2;
  185|   173k|      } else {
  186|   173k|        utf16_output[0] = big_endian ? uint16_t(basic_buffer_swap[i])
  ------------------
  |  Branch (186:27): [Folded, False: 173k]
  ------------------
  187|   173k|                                     : uint16_t(basic_buffer[i]);
  188|   173k|        utf16_output++;
  189|   173k|      }
  190|   241k|    }
  191|  80.5k|  } else {
  192|       |    // here we know that there is an error but we do not handle errors
  193|    808|  }
  194|  3.00M|  return consumed;
  195|  3.00M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_128convert_masked_utf8_to_utf16ILNS_10endiannessE1EEEmPKcmRPDs:
   10|  3.31M|                                    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|  3.31M|  const __m128i swap =
   23|  3.31M|      _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   24|  3.31M|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   25|  3.31M|  const uint16_t input_utf8_end_of_code_point_mask =
   26|  3.31M|      utf8_end_of_code_point_mask & 0xfff;
   27|  3.31M|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (27:7): [True: 11.3k, False: 3.30M]
  ------------------
   28|       |    // We process the data in chunks of 12 bytes.
   29|  11.3k|    __m256i ascii = _mm256_cvtepu8_epi16(in);
   30|  11.3k|    if (big_endian) {
  ------------------
  |  Branch (30:9): [True: 11.3k, Folded]
  ------------------
   31|  11.3k|      const __m256i swap256 = _mm256_setr_epi8(
   32|  11.3k|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   33|  11.3k|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   34|  11.3k|      ascii = _mm256_shuffle_epi8(ascii, swap256);
   35|  11.3k|    }
   36|  11.3k|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf16_output), ascii);
   37|  11.3k|    utf16_output += 12; // We wrote 12 16-bit characters.
   38|  11.3k|    return 12;          // We consumed 12 bytes.
   39|  11.3k|  }
   40|  3.30M|  if (((utf8_end_of_code_point_mask & 0xffff) == 0xaaaa)) {
  ------------------
  |  Branch (40:7): [True: 34.1k, False: 3.26M]
  ------------------
   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|  34.1k|    const __m128i sh =
   45|  34.1k|        _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   46|  34.1k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   47|  34.1k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   48|  34.1k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   49|  34.1k|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   50|  34.1k|    if (big_endian)
  ------------------
  |  Branch (50:9): [True: 34.1k, Folded]
  ------------------
   51|  34.1k|      composed = _mm_shuffle_epi8(composed, swap);
   52|  34.1k|    _mm_storeu_si128((__m128i *)utf16_output, composed);
   53|  34.1k|    utf16_output += 8; // We wrote 16 bytes, 8 code points.
   54|  34.1k|    return 16;
   55|  34.1k|  }
   56|  3.26M|  if (input_utf8_end_of_code_point_mask == 0x924) {
  ------------------
  |  Branch (56:7): [True: 7.02k, False: 3.25M]
  ------------------
   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|  7.02k|    const __m128i sh =
   61|  7.02k|        _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
   62|  7.02k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   63|  7.02k|    const __m128i ascii =
   64|  7.02k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
   65|  7.02k|    const __m128i middlebyte =
   66|  7.02k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
   67|  7.02k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
   68|  7.02k|    const __m128i highbyte =
   69|  7.02k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
   70|  7.02k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
   71|  7.02k|    const __m128i composed =
   72|  7.02k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
   73|  7.02k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
   74|  7.02k|    if (big_endian)
  ------------------
  |  Branch (74:9): [True: 7.02k, Folded]
  ------------------
   75|  7.02k|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
   76|  7.02k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
   77|  7.02k|    utf16_output += 4;
   78|  7.02k|    return 12;
   79|  7.02k|  }
   80|       |
   81|  3.25M|  const uint8_t idx = simdutf::tables::utf8_to_utf16::utf8bigindex
   82|  3.25M|      [input_utf8_end_of_code_point_mask][0];
   83|  3.25M|  const uint8_t consumed = simdutf::tables::utf8_to_utf16::utf8bigindex
   84|  3.25M|      [input_utf8_end_of_code_point_mask][1];
   85|  3.25M|  if (idx < 64) {
  ------------------
  |  Branch (85:7): [True: 2.99M, False: 264k]
  ------------------
   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.99M|    const __m128i sh = _mm_loadu_si128(
   93|  2.99M|        (const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
   94|  2.99M|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   95|  2.99M|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   96|  2.99M|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   97|  2.99M|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   98|  2.99M|    if (big_endian)
  ------------------
  |  Branch (98:9): [True: 2.99M, Folded]
  ------------------
   99|  2.99M|      composed = _mm_shuffle_epi8(composed, swap);
  100|  2.99M|    _mm_storeu_si128((__m128i *)utf16_output, composed);
  101|  2.99M|    utf16_output += 6; // We wrote 12 bytes, 6 code points. There is a potential
  102|       |                       // overflow of 4 bytes.
  103|  2.99M|  } else if (idx < 145) {
  ------------------
  |  Branch (103:14): [True: 175k, False: 89.2k]
  ------------------
  104|       |    // FOUR (4) input code-code units
  105|   175k|    const __m128i sh = _mm_loadu_si128(
  106|   175k|        (const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
  107|   175k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  108|   175k|    const __m128i ascii =
  109|   175k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
  110|   175k|    const __m128i middlebyte =
  111|   175k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
  112|   175k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  113|   175k|    const __m128i highbyte =
  114|   175k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
  115|   175k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
  116|   175k|    const __m128i composed =
  117|   175k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
  118|   175k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
  119|   175k|    if (big_endian)
  ------------------
  |  Branch (119:9): [True: 175k, Folded]
  ------------------
  120|   175k|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
  121|   175k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
  122|   175k|    utf16_output += 4; // Here we overflow by 8 bytes.
  123|   175k|  } else if (idx < 209) {
  ------------------
  |  Branch (123:14): [True: 88.2k, False: 1.02k]
  ------------------
  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|  88.2k|    const __m128i sh = _mm_loadu_si128(
  135|  88.2k|        (const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
  136|  88.2k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  137|  88.2k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
  138|  88.2k|    const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
  139|  88.2k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  140|  88.2k|    __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
  141|       |    // correct for spurious high bit
  142|  88.2k|    const __m128i correct =
  143|  88.2k|        _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
  144|  88.2k|    middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
  145|  88.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|  88.2k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0xff000000));
  149|  88.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|  88.2k|    const __m128i composed =
  155|  88.2k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
  156|  88.2k|                     _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
  157|  88.2k|    const __m128i composedminus =
  158|  88.2k|        _mm_sub_epi32(composed, _mm_set1_epi32(0x10000));
  159|  88.2k|    const __m128i lowtenbits =
  160|  88.2k|        _mm_and_si128(composedminus, _mm_set1_epi32(0x3ff));
  161|       |    // Notice the 0x3ff mask:
  162|  88.2k|    const __m128i hightenbits =
  163|  88.2k|        _mm_and_si128(_mm_srli_epi32(composedminus, 10), _mm_set1_epi32(0x3ff));
  164|  88.2k|    const __m128i lowtenbitsadd =
  165|  88.2k|        _mm_add_epi32(lowtenbits, _mm_set1_epi32(0xDC00));
  166|  88.2k|    const __m128i hightenbitsadd =
  167|  88.2k|        _mm_add_epi32(hightenbits, _mm_set1_epi32(0xD800));
  168|  88.2k|    const __m128i lowtenbitsaddshifted = _mm_slli_epi32(lowtenbitsadd, 16);
  169|  88.2k|    __m128i surrogates = _mm_or_si128(hightenbitsadd, lowtenbitsaddshifted);
  170|  88.2k|    uint32_t basic_buffer[4];
  171|  88.2k|    uint32_t basic_buffer_swap[4];
  172|  88.2k|    if (big_endian) {
  ------------------
  |  Branch (172:9): [True: 88.2k, Folded]
  ------------------
  173|  88.2k|      _mm_storeu_si128((__m128i *)basic_buffer_swap,
  174|  88.2k|                       _mm_shuffle_epi8(composed, swap));
  175|  88.2k|      surrogates = _mm_shuffle_epi8(surrogates, swap);
  176|  88.2k|    }
  177|  88.2k|    _mm_storeu_si128((__m128i *)basic_buffer, composed);
  178|  88.2k|    uint32_t surrogate_buffer[4];
  179|  88.2k|    _mm_storeu_si128((__m128i *)surrogate_buffer, surrogates);
  180|   353k|    for (size_t i = 0; i < 3; i++) {
  ------------------
  |  Branch (180:24): [True: 264k, False: 88.2k]
  ------------------
  181|   264k|      if (basic_buffer[i] > 0x3c00000) {
  ------------------
  |  Branch (181:11): [True: 74.4k, False: 190k]
  ------------------
  182|  74.4k|        utf16_output[0] = uint16_t(surrogate_buffer[i] & 0xffff);
  183|  74.4k|        utf16_output[1] = uint16_t(surrogate_buffer[i] >> 16);
  184|  74.4k|        utf16_output += 2;
  185|   190k|      } else {
  186|   190k|        utf16_output[0] = big_endian ? uint16_t(basic_buffer_swap[i])
  ------------------
  |  Branch (186:27): [True: 190k, Folded]
  ------------------
  187|   190k|                                     : uint16_t(basic_buffer[i]);
  188|   190k|        utf16_output++;
  189|   190k|      }
  190|   264k|    }
  191|  88.2k|  } else {
  192|       |    // here we know that there is an error but we do not handle errors
  193|  1.02k|  }
  194|  3.25M|  return consumed;
  195|  3.26M|}

simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_128convert_masked_utf8_to_utf32EPKcmRPDi:
    9|  3.61M|                                    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.61M|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   22|  3.61M|  const uint16_t input_utf8_end_of_code_point_mask =
   23|  3.61M|      utf8_end_of_code_point_mask & 0xfff;
   24|  3.61M|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (24:7): [True: 10.0k, False: 3.60M]
  ------------------
   25|       |    // We process the data in chunks of 12 bytes.
   26|  10.0k|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output),
   27|  10.0k|                        _mm256_cvtepu8_epi32(in));
   28|  10.0k|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output + 8),
   29|  10.0k|                        _mm256_cvtepu8_epi32(_mm_srli_si128(in, 8)));
   30|  10.0k|    utf32_output += 12; // We wrote 12 32-bit characters.
   31|  10.0k|    return 12;          // We consumed 12 bytes.
   32|  10.0k|  }
   33|  3.60M|  if (((utf8_end_of_code_point_mask & 0xffff) == 0xaaaa)) {
  ------------------
  |  Branch (33:7): [True: 24.8k, False: 3.58M]
  ------------------
   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|  24.8k|    const __m128i sh =
   38|  24.8k|        _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   39|  24.8k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   40|  24.8k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   41|  24.8k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   42|  24.8k|    const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   43|  24.8k|    _mm256_storeu_si256((__m256i *)utf32_output,
   44|  24.8k|                        _mm256_cvtepu16_epi32(composed));
   45|  24.8k|    utf32_output += 8; // We wrote 16 bytes, 8 code points.
   46|  24.8k|    return 16;
   47|  24.8k|  }
   48|  3.58M|  if (input_utf8_end_of_code_point_mask == 0x924) {
  ------------------
  |  Branch (48:7): [True: 4.36k, False: 3.57M]
  ------------------
   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|  4.36k|    const __m128i sh =
   53|  4.36k|        _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
   54|  4.36k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   55|  4.36k|    const __m128i ascii =
   56|  4.36k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
   57|  4.36k|    const __m128i middlebyte =
   58|  4.36k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
   59|  4.36k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
   60|  4.36k|    const __m128i highbyte =
   61|  4.36k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
   62|  4.36k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
   63|  4.36k|    const __m128i composed =
   64|  4.36k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
   65|  4.36k|    _mm_storeu_si128((__m128i *)utf32_output, composed);
   66|  4.36k|    utf32_output += 4;
   67|  4.36k|    return 12;
   68|  4.36k|  }
   69|       |  /// We do not have a fast path available, so we fallback.
   70|       |
   71|  3.57M|  const uint8_t idx =
   72|  3.57M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
   73|  3.57M|  const uint8_t consumed =
   74|  3.57M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
   75|  3.57M|  if (idx < 64) {
  ------------------
  |  Branch (75:7): [True: 3.29M, False: 278k]
  ------------------
   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.29M|    const __m128i sh =
   83|  3.29M|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
   84|  3.29M|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   85|  3.29M|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   86|  3.29M|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   87|  3.29M|    const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   88|  3.29M|    _mm256_storeu_si256((__m256i *)utf32_output,
   89|  3.29M|                        _mm256_cvtepu16_epi32(composed));
   90|  3.29M|    utf32_output += 6; // We wrote 24 bytes, 6 code points. There is a potential
   91|       |    // overflow of 32 - 24 = 8 bytes.
   92|  3.29M|  } else if (idx < 145) {
  ------------------
  |  Branch (92:14): [True: 197k, False: 81.0k]
  ------------------
   93|       |    // FOUR (4) input code-code units
   94|   197k|    const __m128i sh =
   95|   197k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
   96|   197k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   97|   197k|    const __m128i ascii =
   98|   197k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
   99|   197k|    const __m128i middlebyte =
  100|   197k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
  101|   197k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  102|   197k|    const __m128i highbyte =
  103|   197k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
  104|   197k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
  105|   197k|    const __m128i composed =
  106|   197k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
  107|   197k|    _mm_storeu_si128((__m128i *)utf32_output, composed);
  108|   197k|    utf32_output += 4;
  109|   197k|  } else if (idx < 209) {
  ------------------
  |  Branch (109:14): [True: 80.1k, False: 930]
  ------------------
  110|       |    // TWO (2) input code-code units
  111|  80.1k|    const __m128i sh =
  112|  80.1k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
  113|  80.1k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  114|  80.1k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
  115|  80.1k|    const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
  116|  80.1k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  117|  80.1k|    __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
  118|       |    // correct for spurious high bit
  119|  80.1k|    const __m128i correct =
  120|  80.1k|        _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
  121|  80.1k|    middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
  122|  80.1k|    const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4);
  123|  80.1k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0x07000000));
  124|  80.1k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6);
  125|  80.1k|    const __m128i composed =
  126|  80.1k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
  127|  80.1k|                     _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
  128|  80.1k|    _mm_storeu_si128((__m128i *)utf32_output, composed);
  129|  80.1k|    utf32_output +=
  130|  80.1k|        3; // We wrote 3 * 4 bytes, there is a potential overflow of 4 bytes.
  131|  80.1k|  } else {
  132|       |    // here we know that there is an error but we do not handle errors
  133|    930|  }
  134|  3.57M|  return consumed;
  135|  3.58M|}

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

_ZNK7simdutf7haswell14implementation13validate_utf8EPKcm:
  264|  3.42k|implementation::validate_utf8(const char *buf, size_t len) const noexcept {
  265|  3.42k|  return haswell::utf8_validation::generic_validate_utf8(buf, len);
  266|  3.42k|}
_ZNK7simdutf7haswell14implementation25validate_utf8_with_errorsEPKcm:
  271|  3.42k|    const char *buf, size_t len) const noexcept {
  272|  3.42k|  return haswell::utf8_validation::generic_validate_utf8_with_errors(buf, len);
  273|  3.42k|}
_ZNK7simdutf7haswell14implementation16validate_utf16leEPKDsm:
  310|  1.39k|                                 size_t len) const noexcept {
  311|  1.39k|  if (simdutf_unlikely(len == 0)) {
  ------------------
  |  |   92|  1.39k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (92:33): [True: 16, False: 1.37k]
  |  |  ------------------
  ------------------
  312|       |    // empty input is valid UTF-16. protect the implementation from
  313|       |    // handling nullptr
  314|     16|    return true;
  315|     16|  }
  316|  1.37k|  const auto res =
  317|  1.37k|      haswell::utf16::validate_utf16_with_errors<endianness::LITTLE>(buf, len);
  318|  1.37k|  if (res.is_err()) {
  ------------------
  |  Branch (318:7): [True: 124, False: 1.25k]
  ------------------
  319|    124|    return false;
  320|    124|  }
  321|       |
  322|  1.25k|  if (res.count == len) {
  ------------------
  |  Branch (322:7): [True: 0, False: 1.25k]
  ------------------
  323|      0|    return true;
  324|      0|  }
  325|       |
  326|  1.25k|  return scalar::utf16::validate<endianness::LITTLE>(buf + res.count,
  327|  1.25k|                                                     len - res.count);
  328|  1.25k|}
_ZNK7simdutf7haswell14implementation16validate_utf16beEPKDsm:
  334|  1.30k|                                 size_t len) const noexcept {
  335|  1.30k|  if (simdutf_unlikely(len == 0)) {
  ------------------
  |  |   92|  1.30k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (92:33): [True: 17, False: 1.28k]
  |  |  ------------------
  ------------------
  336|       |    // empty input is valid UTF-16. protect the implementation from
  337|       |    // handling nullptr
  338|     17|    return true;
  339|     17|  }
  340|  1.28k|  const auto res =
  341|  1.28k|      haswell::utf16::validate_utf16_with_errors<endianness::BIG>(buf, len);
  342|  1.28k|  if (res.is_err()) {
  ------------------
  |  Branch (342:7): [True: 106, False: 1.18k]
  ------------------
  343|    106|    return false;
  344|    106|  }
  345|       |
  346|  1.18k|  if (res.count == len) {
  ------------------
  |  Branch (346:7): [True: 0, False: 1.18k]
  ------------------
  347|      0|    return true;
  348|      0|  }
  349|       |
  350|  1.18k|  return scalar::utf16::validate<endianness::BIG>(buf + res.count,
  351|  1.18k|                                                  len - res.count);
  352|  1.18k|}
_ZNK7simdutf7haswell14implementation28validate_utf16le_with_errorsEPKDsm:
  355|  1.39k|    const char16_t *buf, size_t len) const noexcept {
  356|       |
  357|  1.39k|  const result res =
  358|  1.39k|      haswell::utf16::validate_utf16_with_errors<endianness::LITTLE>(buf, len);
  359|  1.39k|  if (res.count != len) {
  ------------------
  |  Branch (359:7): [True: 1.37k, False: 16]
  ------------------
  360|  1.37k|    const result scalar_res =
  361|  1.37k|        scalar::utf16::validate_with_errors<endianness::LITTLE>(
  362|  1.37k|            buf + res.count, len - res.count);
  363|  1.37k|    return result(scalar_res.error, res.count + scalar_res.count);
  364|  1.37k|  } else {
  365|     16|    return res;
  366|     16|  }
  367|  1.39k|}
_ZNK7simdutf7haswell14implementation28validate_utf16be_with_errorsEPKDsm:
  370|  1.30k|    const char16_t *buf, size_t len) const noexcept {
  371|  1.30k|  const result res =
  372|  1.30k|      haswell::utf16::validate_utf16_with_errors<endianness::BIG>(buf, len);
  373|  1.30k|  if (res.count != len) {
  ------------------
  |  Branch (373:7): [True: 1.28k, False: 17]
  ------------------
  374|  1.28k|    const result scalar_res =
  375|  1.28k|        scalar::utf16::validate_with_errors<endianness::BIG>(buf + res.count,
  376|  1.28k|                                                             len - res.count);
  377|  1.28k|    return result(scalar_res.error, res.count + scalar_res.count);
  378|  1.28k|  } else {
  379|     17|    return res;
  380|     17|  }
  381|  1.30k|}
_ZNK7simdutf7haswell14implementation14validate_utf32EPKDim:
  396|  2.49k|implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept {
  397|  2.49k|  return utf32::validate(buf, len);
  398|  2.49k|}
_ZNK7simdutf7haswell14implementation26validate_utf32_with_errorsEPKDim:
  403|  2.49k|    const char32_t *buf, size_t len) const noexcept {
  404|  2.49k|  return utf32::validate_with_errors(buf, len);
  405|  2.49k|}
_ZNK7simdutf7haswell14implementation22convert_latin1_to_utf8EPKcmPc:
  410|    614|    const char *buf, size_t len, char *utf8_output) const noexcept {
  411|    614|  std::pair<const char *, char *> ret =
  412|    614|      avx2_convert_latin1_to_utf8(buf, len, utf8_output);
  413|    614|  size_t converted_chars = ret.second - utf8_output;
  414|       |
  415|    614|  if (ret.first != buf + len) {
  ------------------
  |  Branch (415:7): [True: 610, False: 4]
  ------------------
  416|    610|    const size_t scalar_converted_chars = scalar::latin1_to_utf8::convert(
  417|    610|        ret.first, len - (ret.first - buf), ret.second);
  418|    610|    converted_chars += scalar_converted_chars;
  419|    610|  }
  420|       |
  421|    614|  return converted_chars;
  422|    614|}
_ZNK7simdutf7haswell14implementation25convert_latin1_to_utf16leEPKcmPDs:
  427|     88|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  428|     88|  std::pair<const char *, char16_t *> ret =
  429|     88|      avx2_convert_latin1_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
  430|     88|  if (ret.first == nullptr) {
  ------------------
  |  Branch (430:7): [True: 4, False: 84]
  ------------------
  431|      4|    return 0;
  432|      4|  }
  433|     84|  size_t converted_chars = ret.second - utf16_output;
  434|     84|  if (ret.first != buf + len) {
  ------------------
  |  Branch (434:7): [True: 70, False: 14]
  ------------------
  435|     70|    const size_t scalar_converted_chars =
  436|     70|        scalar::latin1_to_utf16::convert<endianness::LITTLE>(
  437|     70|            ret.first, len - (ret.first - buf), ret.second);
  438|     70|    if (scalar_converted_chars == 0) {
  ------------------
  |  Branch (438:9): [True: 0, False: 70]
  ------------------
  439|      0|      return 0;
  440|      0|    }
  441|     70|    converted_chars += scalar_converted_chars;
  442|     70|  }
  443|     84|  return converted_chars;
  444|     84|}
_ZNK7simdutf7haswell14implementation25convert_latin1_to_utf16beEPKcmPDs:
  447|    100|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  448|    100|  std::pair<const char *, char16_t *> ret =
  449|    100|      avx2_convert_latin1_to_utf16<endianness::BIG>(buf, len, utf16_output);
  450|    100|  if (ret.first == nullptr) {
  ------------------
  |  Branch (450:7): [True: 4, False: 96]
  ------------------
  451|      4|    return 0;
  452|      4|  }
  453|     96|  size_t converted_chars = ret.second - utf16_output;
  454|     96|  if (ret.first != buf + len) {
  ------------------
  |  Branch (454:7): [True: 80, False: 16]
  ------------------
  455|     80|    const size_t scalar_converted_chars =
  456|     80|        scalar::latin1_to_utf16::convert<endianness::BIG>(
  457|     80|            ret.first, len - (ret.first - buf), ret.second);
  458|     80|    if (scalar_converted_chars == 0) {
  ------------------
  |  Branch (458:9): [True: 0, False: 80]
  ------------------
  459|      0|      return 0;
  460|      0|    }
  461|     80|    converted_chars += scalar_converted_chars;
  462|     80|  }
  463|     96|  return converted_chars;
  464|     96|}
_ZNK7simdutf7haswell14implementation23convert_latin1_to_utf32EPKcmPDi:
  469|    126|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  470|    126|  std::pair<const char *, char32_t *> ret =
  471|    126|      avx2_convert_latin1_to_utf32(buf, len, utf32_output);
  472|    126|  if (ret.first == nullptr) {
  ------------------
  |  Branch (472:7): [True: 4, False: 122]
  ------------------
  473|      4|    return 0;
  474|      4|  }
  475|    122|  size_t converted_chars = ret.second - utf32_output;
  476|    122|  if (ret.first != buf + len) {
  ------------------
  |  Branch (476:7): [True: 80, False: 42]
  ------------------
  477|     80|    const size_t scalar_converted_chars = scalar::latin1_to_utf32::convert(
  478|     80|        ret.first, len - (ret.first - buf), ret.second);
  479|     80|    if (scalar_converted_chars == 0) {
  ------------------
  |  Branch (479:9): [True: 0, False: 80]
  ------------------
  480|      0|      return 0;
  481|      0|    }
  482|     80|    converted_chars += scalar_converted_chars;
  483|     80|  }
  484|    122|  return converted_chars;
  485|    122|}
_ZNK7simdutf7haswell14implementation22convert_utf8_to_latin1EPKcmPc:
  490|    576|    const char *buf, size_t len, char *latin1_output) const noexcept {
  491|    576|  utf8_to_latin1::validating_transcoder converter;
  492|    576|  return converter.convert(buf, len, latin1_output);
  493|    576|}
_ZNK7simdutf7haswell14implementation34convert_utf8_to_latin1_with_errorsEPKcmPc:
  496|    500|    const char *buf, size_t len, char *latin1_output) const noexcept {
  497|    500|  utf8_to_latin1::validating_transcoder converter;
  498|    500|  return converter.convert_with_errors(buf, len, latin1_output);
  499|    500|}
_ZNK7simdutf7haswell14implementation23convert_utf8_to_utf16leEPKcmPDs:
  509|    754|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  510|    754|  utf8_to_utf16::validating_transcoder converter;
  511|    754|  return converter.convert<endianness::LITTLE>(buf, len, utf16_output);
  512|    754|}
_ZNK7simdutf7haswell14implementation23convert_utf8_to_utf16beEPKcmPDs:
  515|    758|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  516|    758|  utf8_to_utf16::validating_transcoder converter;
  517|    758|  return converter.convert<endianness::BIG>(buf, len, utf16_output);
  518|    758|}
_ZNK7simdutf7haswell14implementation35convert_utf8_to_utf16le_with_errorsEPKcmPDs:
  521|    568|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  522|    568|  utf8_to_utf16::validating_transcoder converter;
  523|    568|  return converter.convert_with_errors<endianness::LITTLE>(buf, len,
  524|    568|                                                           utf16_output);
  525|    568|}
_ZNK7simdutf7haswell14implementation35convert_utf8_to_utf16be_with_errorsEPKcmPDs:
  528|    758|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  529|    758|  utf8_to_utf16::validating_transcoder converter;
  530|    758|  return converter.convert_with_errors<endianness::BIG>(buf, len, utf16_output);
  531|    758|}
_ZNK7simdutf7haswell14implementation29convert_valid_utf8_to_utf16leEPKcmPDs:
  534|    438|    const char *input, size_t size, char16_t *utf16_output) const noexcept {
  535|    438|  return utf8_to_utf16::convert_valid<endianness::LITTLE>(input, size,
  536|    438|                                                          utf16_output);
  537|    438|}
_ZNK7simdutf7haswell14implementation29convert_valid_utf8_to_utf16beEPKcmPDs:
  540|    456|    const char *input, size_t size, char16_t *utf16_output) const noexcept {
  541|    456|  return utf8_to_utf16::convert_valid<endianness::BIG>(input, size,
  542|    456|                                                       utf16_output);
  543|    456|}
_ZNK7simdutf7haswell14implementation21convert_utf8_to_utf32EPKcmPDi:
  548|    758|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  549|    758|  utf8_to_utf32::validating_transcoder converter;
  550|    758|  return converter.convert(buf, len, utf32_output);
  551|    758|}
_ZNK7simdutf7haswell14implementation33convert_utf8_to_utf32_with_errorsEPKcmPDi:
  554|    764|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  555|    764|  utf8_to_utf32::validating_transcoder converter;
  556|    764|  return converter.convert_with_errors(buf, len, utf32_output);
  557|    764|}
_ZNK7simdutf7haswell14implementation27convert_valid_utf8_to_utf32EPKcmPDi:
  560|    454|    const char *input, size_t size, char32_t *utf32_output) const noexcept {
  561|    454|  return utf8_to_utf32::convert_valid(input, size, utf32_output);
  562|    454|}
_ZNK7simdutf7haswell14implementation25convert_utf16le_to_latin1EPKDsmPc:
  567|    132|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  568|    132|  std::pair<const char16_t *, char *> ret =
  569|    132|      haswell::avx2_convert_utf16_to_latin1<endianness::LITTLE>(buf, len,
  570|    132|                                                                latin1_output);
  571|    132|  if (ret.first == nullptr) {
  ------------------
  |  Branch (571:7): [True: 48, False: 84]
  ------------------
  572|     48|    return 0;
  573|     48|  }
  574|     84|  size_t saved_bytes = ret.second - latin1_output;
  575|     84|  if (ret.first != buf + len) {
  ------------------
  |  Branch (575:7): [True: 70, False: 14]
  ------------------
  576|     70|    const size_t scalar_saved_bytes =
  577|     70|        scalar::utf16_to_latin1::convert<endianness::LITTLE>(
  578|     70|            ret.first, len - (ret.first - buf), ret.second);
  579|     70|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (579:9): [True: 44, False: 26]
  ------------------
  580|     44|      return 0;
  581|     44|    }
  582|     26|    saved_bytes += scalar_saved_bytes;
  583|     26|  }
  584|     40|  return saved_bytes;
  585|     84|}
_ZNK7simdutf7haswell14implementation25convert_utf16be_to_latin1EPKDsmPc:
  588|    102|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  589|    102|  std::pair<const char16_t *, char *> ret =
  590|    102|      haswell::avx2_convert_utf16_to_latin1<endianness::BIG>(buf, len,
  591|    102|                                                             latin1_output);
  592|    102|  if (ret.first == nullptr) {
  ------------------
  |  Branch (592:7): [True: 24, False: 78]
  ------------------
  593|     24|    return 0;
  594|     24|  }
  595|     78|  size_t saved_bytes = ret.second - latin1_output;
  596|     78|  if (ret.first != buf + len) {
  ------------------
  |  Branch (596:7): [True: 64, False: 14]
  ------------------
  597|     64|    const size_t scalar_saved_bytes =
  598|     64|        scalar::utf16_to_latin1::convert<endianness::BIG>(
  599|     64|            ret.first, len - (ret.first - buf), ret.second);
  600|     64|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (600:9): [True: 36, False: 28]
  ------------------
  601|     36|      return 0;
  602|     36|    }
  603|     28|    saved_bytes += scalar_saved_bytes;
  604|     28|  }
  605|     42|  return saved_bytes;
  606|     78|}
_ZNK7simdutf7haswell14implementation37convert_utf16le_to_latin1_with_errorsEPKDsmPc:
  610|    282|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  611|    282|  std::pair<result, char *> ret =
  612|    282|      avx2_convert_utf16_to_latin1_with_errors<endianness::LITTLE>(
  613|    282|          buf, len, latin1_output);
  614|    282|  if (ret.first.error) {
  ------------------
  |  Branch (614:7): [True: 134, False: 148]
  ------------------
  615|    134|    return ret.first;
  616|    134|  } // Can return directly since scalar fallback already found correct
  617|       |    // ret.first.count
  618|    148|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (618:7): [True: 110, False: 38]
  ------------------
  619|    110|    result scalar_res =
  620|    110|        scalar::utf16_to_latin1::convert_with_errors<endianness::LITTLE>(
  621|    110|            buf + ret.first.count, len - ret.first.count, ret.second);
  622|    110|    if (scalar_res.error) {
  ------------------
  |  Branch (622:9): [True: 42, False: 68]
  ------------------
  623|     42|      scalar_res.count += ret.first.count;
  624|     42|      return scalar_res;
  625|     68|    } else {
  626|     68|      ret.second += scalar_res.count;
  627|     68|    }
  628|    110|  }
  629|    106|  ret.first.count =
  630|    106|      ret.second -
  631|    106|      latin1_output; // Set count to the number of 8-bit code units written
  632|    106|  return ret.first;
  633|    148|}
_ZNK7simdutf7haswell14implementation37convert_utf16be_to_latin1_with_errorsEPKDsmPc:
  637|    248|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  638|    248|  std::pair<result, char *> ret =
  639|    248|      avx2_convert_utf16_to_latin1_with_errors<endianness::BIG>(buf, len,
  640|    248|                                                                latin1_output);
  641|    248|  if (ret.first.error) {
  ------------------
  |  Branch (641:7): [True: 104, False: 144]
  ------------------
  642|    104|    return ret.first;
  643|    104|  } // Can return directly since scalar fallback already found correct
  644|       |    // ret.first.count
  645|    144|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (645:7): [True: 108, False: 36]
  ------------------
  646|    108|    result scalar_res =
  647|    108|        scalar::utf16_to_latin1::convert_with_errors<endianness::BIG>(
  648|    108|            buf + ret.first.count, len - ret.first.count, ret.second);
  649|    108|    if (scalar_res.error) {
  ------------------
  |  Branch (649:9): [True: 56, False: 52]
  ------------------
  650|     56|      scalar_res.count += ret.first.count;
  651|     56|      return scalar_res;
  652|     56|    } else {
  653|     52|      ret.second += scalar_res.count;
  654|     52|    }
  655|    108|  }
  656|     88|  ret.first.count =
  657|     88|      ret.second -
  658|     88|      latin1_output; // Set count to the number of 8-bit code units written
  659|     88|  return ret.first;
  660|    144|}
_ZNK7simdutf7haswell14implementation23convert_utf16le_to_utf8EPKDsmPc:
  677|    838|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  678|    838|  std::pair<const char16_t *, char *> ret =
  679|    838|      haswell::avx2_convert_utf16_to_utf8<endianness::LITTLE>(buf, len,
  680|    838|                                                              utf8_output);
  681|    838|  if (ret.first == nullptr) {
  ------------------
  |  Branch (681:7): [True: 128, False: 710]
  ------------------
  682|    128|    return 0;
  683|    128|  }
  684|    710|  size_t saved_bytes = ret.second - utf8_output;
  685|    710|  if (ret.first != buf + len) {
  ------------------
  |  Branch (685:7): [True: 708, False: 2]
  ------------------
  686|    708|    const size_t scalar_saved_bytes =
  687|    708|        scalar::utf16_to_utf8::convert<endianness::LITTLE>(
  688|    708|            ret.first, len - (ret.first - buf), ret.second);
  689|    708|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (689:9): [True: 88, False: 620]
  ------------------
  690|     88|      return 0;
  691|     88|    }
  692|    620|    saved_bytes += scalar_saved_bytes;
  693|    620|  }
  694|    622|  return saved_bytes;
  695|    710|}
_ZNK7simdutf7haswell14implementation23convert_utf16be_to_utf8EPKDsmPc:
  698|    742|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  699|    742|  std::pair<const char16_t *, char *> ret =
  700|    742|      haswell::avx2_convert_utf16_to_utf8<endianness::BIG>(buf, len,
  701|    742|                                                           utf8_output);
  702|    742|  if (ret.first == nullptr) {
  ------------------
  |  Branch (702:7): [True: 100, False: 642]
  ------------------
  703|    100|    return 0;
  704|    100|  }
  705|    642|  size_t saved_bytes = ret.second - utf8_output;
  706|    642|  if (ret.first != buf + len) {
  ------------------
  |  Branch (706:7): [True: 640, False: 2]
  ------------------
  707|    640|    const size_t scalar_saved_bytes =
  708|    640|        scalar::utf16_to_utf8::convert<endianness::BIG>(
  709|    640|            ret.first, len - (ret.first - buf), ret.second);
  710|    640|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (710:9): [True: 60, False: 580]
  ------------------
  711|     60|      return 0;
  712|     60|    }
  713|    580|    saved_bytes += scalar_saved_bytes;
  714|    580|  }
  715|    582|  return saved_bytes;
  716|    642|}
_ZNK7simdutf7haswell14implementation35convert_utf16le_to_utf8_with_errorsEPKDsmPc:
  719|    678|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  720|       |  // ret.first.count is always the position in the buffer, not the number of
  721|       |  // code units written even if finished
  722|    678|  std::pair<result, char *> ret =
  723|    678|      haswell::avx2_convert_utf16_to_utf8_with_errors<endianness::LITTLE>(
  724|    678|          buf, len, utf8_output);
  725|    678|  if (ret.first.error) {
  ------------------
  |  Branch (725:7): [True: 188, False: 490]
  ------------------
  726|    188|    return ret.first;
  727|    188|  } // Can return directly since scalar fallback already found correct
  728|       |    // ret.first.count
  729|    490|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (729:7): [True: 486, False: 4]
  ------------------
  730|    486|    result scalar_res =
  731|    486|        scalar::utf16_to_utf8::convert_with_errors<endianness::LITTLE>(
  732|    486|            buf + ret.first.count, len - ret.first.count, ret.second);
  733|    486|    if (scalar_res.error) {
  ------------------
  |  Branch (733:9): [True: 94, False: 392]
  ------------------
  734|     94|      scalar_res.count += ret.first.count;
  735|     94|      return scalar_res;
  736|    392|    } else {
  737|    392|      ret.second += scalar_res.count;
  738|    392|    }
  739|    486|  }
  740|    396|  ret.first.count =
  741|    396|      ret.second -
  742|    396|      utf8_output; // Set count to the number of 8-bit code units written
  743|    396|  return ret.first;
  744|    490|}
_ZNK7simdutf7haswell14implementation35convert_utf16be_to_utf8_with_errorsEPKDsmPc:
  747|    716|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  748|       |  // ret.first.count is always the position in the buffer, not the number of
  749|       |  // code units written even if finished
  750|    716|  std::pair<result, char *> ret =
  751|    716|      haswell::avx2_convert_utf16_to_utf8_with_errors<endianness::BIG>(
  752|    716|          buf, len, utf8_output);
  753|    716|  if (ret.first.error) {
  ------------------
  |  Branch (753:7): [True: 200, False: 516]
  ------------------
  754|    200|    return ret.first;
  755|    200|  } // Can return directly since scalar fallback already found correct
  756|       |    // ret.first.count
  757|    516|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (757:7): [True: 512, False: 4]
  ------------------
  758|    512|    result scalar_res =
  759|    512|        scalar::utf16_to_utf8::convert_with_errors<endianness::BIG>(
  760|    512|            buf + ret.first.count, len - ret.first.count, ret.second);
  761|    512|    if (scalar_res.error) {
  ------------------
  |  Branch (761:9): [True: 94, False: 418]
  ------------------
  762|     94|      scalar_res.count += ret.first.count;
  763|     94|      return scalar_res;
  764|    418|    } else {
  765|    418|      ret.second += scalar_res.count;
  766|    418|    }
  767|    512|  }
  768|    422|  ret.first.count =
  769|    422|      ret.second -
  770|    422|      utf8_output; // Set count to the number of 8-bit code units written
  771|    422|  return ret.first;
  772|    516|}
_ZNK7simdutf7haswell14implementation29convert_valid_utf16le_to_utf8EPKDsmPc:
  775|    428|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  776|    428|  return convert_utf16le_to_utf8(buf, len, utf8_output);
  777|    428|}
_ZNK7simdutf7haswell14implementation29convert_valid_utf16be_to_utf8EPKDsmPc:
  780|    400|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  781|    400|  return convert_utf16be_to_utf8(buf, len, utf8_output);
  782|    400|}
_ZNK7simdutf7haswell14implementation21convert_utf32_to_utf8EPKDimPc:
  787|    860|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  788|    860|  std::pair<const char32_t *, char *> ret =
  789|    860|      avx2_convert_utf32_to_utf8(buf, len, utf8_output);
  790|    860|  if (ret.first == nullptr) {
  ------------------
  |  Branch (790:7): [True: 280, False: 580]
  ------------------
  791|    280|    return 0;
  792|    280|  }
  793|    580|  size_t saved_bytes = ret.second - utf8_output;
  794|    580|  if (ret.first != buf + len) {
  ------------------
  |  Branch (794:7): [True: 578, False: 2]
  ------------------
  795|    578|    const size_t scalar_saved_bytes = scalar::utf32_to_utf8::convert(
  796|    578|        ret.first, len - (ret.first - buf), ret.second);
  797|    578|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (797:9): [True: 256, False: 322]
  ------------------
  798|    256|      return 0;
  799|    256|    }
  800|    322|    saved_bytes += scalar_saved_bytes;
  801|    322|  }
  802|    324|  return saved_bytes;
  803|    580|}
_ZNK7simdutf7haswell14implementation23convert_utf32_to_latin1EPKDimPc:
  808|    178|    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
  809|    178|  std::pair<const char32_t *, char *> ret =
  810|    178|      avx2_convert_utf32_to_latin1(buf, len, latin1_output);
  811|    178|  if (ret.first == nullptr) {
  ------------------
  |  Branch (811:7): [True: 16, False: 162]
  ------------------
  812|     16|    return 0;
  813|     16|  }
  814|    162|  size_t saved_bytes = ret.second - latin1_output;
  815|    162|  if (ret.first != buf + len) {
  ------------------
  |  Branch (815:7): [True: 142, False: 20]
  ------------------
  816|    142|    const size_t scalar_saved_bytes = scalar::utf32_to_latin1::convert(
  817|    142|        ret.first, len - (ret.first - buf), ret.second);
  818|    142|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (818:9): [True: 112, False: 30]
  ------------------
  819|    112|      return 0;
  820|    112|    }
  821|     30|    saved_bytes += scalar_saved_bytes;
  822|     30|  }
  823|     50|  return saved_bytes;
  824|    162|}
_ZNK7simdutf7haswell14implementation35convert_utf32_to_latin1_with_errorsEPKDimPc:
  827|    408|    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
  828|       |  // ret.first.count is always the position in the buffer, not the number of
  829|       |  // code units written even if finished
  830|    408|  std::pair<result, char *> ret =
  831|    408|      avx2_convert_utf32_to_latin1_with_errors(buf, len, latin1_output);
  832|    408|  if (ret.first.count != len) {
  ------------------
  |  Branch (832:7): [True: 388, False: 20]
  ------------------
  833|    388|    result scalar_res = scalar::utf32_to_latin1::convert_with_errors(
  834|    388|        buf + ret.first.count, len - ret.first.count, ret.second);
  835|    388|    if (scalar_res.error) {
  ------------------
  |  Branch (835:9): [True: 364, False: 24]
  ------------------
  836|    364|      scalar_res.count += ret.first.count;
  837|    364|      return scalar_res;
  838|    364|    } else {
  839|     24|      ret.second += scalar_res.count;
  840|     24|    }
  841|    388|  }
  842|     44|  ret.first.count =
  843|     44|      ret.second -
  844|     44|      latin1_output; // Set count to the number of 8-bit code units written
  845|     44|  return ret.first;
  846|    408|}
_ZNK7simdutf7haswell14implementation33convert_utf32_to_utf8_with_errorsEPKDimPc:
  856|    892|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  857|       |  // ret.first.count is always the position in the buffer, not the number of
  858|       |  // code units written even if finished
  859|    892|  std::pair<result, char *> ret =
  860|    892|      haswell::avx2_convert_utf32_to_utf8_with_errors(buf, len, utf8_output);
  861|    892|  if (ret.first.count != len) {
  ------------------
  |  Branch (861:7): [True: 888, False: 4]
  ------------------
  862|    888|    result scalar_res = scalar::utf32_to_utf8::convert_with_errors(
  863|    888|        buf + ret.first.count, len - ret.first.count, ret.second);
  864|    888|    if (scalar_res.error) {
  ------------------
  |  Branch (864:9): [True: 670, False: 218]
  ------------------
  865|    670|      scalar_res.count += ret.first.count;
  866|    670|      return scalar_res;
  867|    670|    } else {
  868|    218|      ret.second += scalar_res.count;
  869|    218|    }
  870|    888|  }
  871|    222|  ret.first.count =
  872|    222|      ret.second -
  873|    222|      utf8_output; // Set count to the number of 8-bit code units written
  874|    222|  return ret.first;
  875|    892|}
_ZNK7simdutf7haswell14implementation24convert_utf16le_to_utf32EPKDsmPDi:
  880|    414|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  881|    414|  std::pair<const char16_t *, char32_t *> ret =
  882|    414|      haswell::avx2_convert_utf16_to_utf32<endianness::LITTLE>(buf, len,
  883|    414|                                                               utf32_output);
  884|    414|  if (ret.first == nullptr) {
  ------------------
  |  Branch (884:7): [True: 104, False: 310]
  ------------------
  885|    104|    return 0;
  886|    104|  }
  887|    310|  size_t saved_bytes = ret.second - utf32_output;
  888|    310|  if (ret.first != buf + len) {
  ------------------
  |  Branch (888:7): [True: 256, False: 54]
  ------------------
  889|    256|    const size_t scalar_saved_bytes =
  890|    256|        scalar::utf16_to_utf32::convert<endianness::LITTLE>(
  891|    256|            ret.first, len - (ret.first - buf), ret.second);
  892|    256|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (892:9): [True: 78, False: 178]
  ------------------
  893|     78|      return 0;
  894|     78|    }
  895|    178|    saved_bytes += scalar_saved_bytes;
  896|    178|  }
  897|    232|  return saved_bytes;
  898|    310|}
_ZNK7simdutf7haswell14implementation24convert_utf16be_to_utf32EPKDsmPDi:
  901|    384|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  902|    384|  std::pair<const char16_t *, char32_t *> ret =
  903|    384|      haswell::avx2_convert_utf16_to_utf32<endianness::BIG>(buf, len,
  904|    384|                                                            utf32_output);
  905|    384|  if (ret.first == nullptr) {
  ------------------
  |  Branch (905:7): [True: 76, False: 308]
  ------------------
  906|     76|    return 0;
  907|     76|  }
  908|    308|  size_t saved_bytes = ret.second - utf32_output;
  909|    308|  if (ret.first != buf + len) {
  ------------------
  |  Branch (909:7): [True: 264, False: 44]
  ------------------
  910|    264|    const size_t scalar_saved_bytes =
  911|    264|        scalar::utf16_to_utf32::convert<endianness::BIG>(
  912|    264|            ret.first, len - (ret.first - buf), ret.second);
  913|    264|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (913:9): [True: 80, False: 184]
  ------------------
  914|     80|      return 0;
  915|     80|    }
  916|    184|    saved_bytes += scalar_saved_bytes;
  917|    184|  }
  918|    228|  return saved_bytes;
  919|    308|}
_ZNK7simdutf7haswell14implementation36convert_utf16le_to_utf32_with_errorsEPKDsmPDi:
  922|    408|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  923|       |  // ret.first.count is always the position in the buffer, not the number of
  924|       |  // code units written even if finished
  925|    408|  std::pair<result, char32_t *> ret =
  926|    408|      haswell::avx2_convert_utf16_to_utf32_with_errors<endianness::LITTLE>(
  927|    408|          buf, len, utf32_output);
  928|    408|  if (ret.first.error) {
  ------------------
  |  Branch (928:7): [True: 136, False: 272]
  ------------------
  929|    136|    return ret.first;
  930|    136|  } // Can return directly since scalar fallback already found correct
  931|       |    // ret.first.count
  932|    272|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (932:7): [True: 236, False: 36]
  ------------------
  933|    236|    result scalar_res =
  934|    236|        scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
  935|    236|            buf + ret.first.count, len - ret.first.count, ret.second);
  936|    236|    if (scalar_res.error) {
  ------------------
  |  Branch (936:9): [True: 94, False: 142]
  ------------------
  937|     94|      scalar_res.count += ret.first.count;
  938|     94|      return scalar_res;
  939|    142|    } else {
  940|    142|      ret.second += scalar_res.count;
  941|    142|    }
  942|    236|  }
  943|    178|  ret.first.count =
  944|    178|      ret.second -
  945|    178|      utf32_output; // Set count to the number of 8-bit code units written
  946|    178|  return ret.first;
  947|    272|}
_ZNK7simdutf7haswell14implementation36convert_utf16be_to_utf32_with_errorsEPKDsmPDi:
  950|    362|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  951|       |  // ret.first.count is always the position in the buffer, not the number of
  952|       |  // code units written even if finished
  953|    362|  std::pair<result, char32_t *> ret =
  954|    362|      haswell::avx2_convert_utf16_to_utf32_with_errors<endianness::BIG>(
  955|    362|          buf, len, utf32_output);
  956|    362|  if (ret.first.error) {
  ------------------
  |  Branch (956:7): [True: 72, False: 290]
  ------------------
  957|     72|    return ret.first;
  958|     72|  } // Can return directly since scalar fallback already found correct
  959|       |    // ret.first.count
  960|    290|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (960:7): [True: 262, False: 28]
  ------------------
  961|    262|    result scalar_res =
  962|    262|        scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
  963|    262|            buf + ret.first.count, len - ret.first.count, ret.second);
  964|    262|    if (scalar_res.error) {
  ------------------
  |  Branch (964:9): [True: 116, False: 146]
  ------------------
  965|    116|      scalar_res.count += ret.first.count;
  966|    116|      return scalar_res;
  967|    146|    } else {
  968|    146|      ret.second += scalar_res.count;
  969|    146|    }
  970|    262|  }
  971|    174|  ret.first.count =
  972|    174|      ret.second -
  973|    174|      utf32_output; // Set count to the number of 8-bit code units written
  974|    174|  return ret.first;
  975|    290|}
_ZNK7simdutf7haswell14implementation27convert_valid_utf32_to_utf8EPKDimPc:
  980|    224|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  981|    224|  return convert_utf32_to_utf8(buf, len, utf8_output);
  982|    224|}
_ZNK7simdutf7haswell14implementation24convert_utf32_to_utf16leEPKDimPDs:
  987|    738|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
  988|    738|  std::pair<const char32_t *, char16_t *> ret =
  989|    738|      avx2_convert_utf32_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
  990|    738|  if (ret.first == nullptr) {
  ------------------
  |  Branch (990:7): [True: 184, False: 554]
  ------------------
  991|    184|    return 0;
  992|    184|  }
  993|    554|  size_t saved_bytes = ret.second - utf16_output;
  994|    554|  if (ret.first != buf + len) {
  ------------------
  |  Branch (994:7): [True: 550, False: 4]
  ------------------
  995|    550|    const size_t scalar_saved_bytes =
  996|    550|        scalar::utf32_to_utf16::convert<endianness::LITTLE>(
  997|    550|            ret.first, len - (ret.first - buf), ret.second);
  998|    550|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (998:9): [True: 168, False: 382]
  ------------------
  999|    168|      return 0;
 1000|    168|    }
 1001|    382|    saved_bytes += scalar_saved_bytes;
 1002|    382|  }
 1003|    386|  return saved_bytes;
 1004|    554|}
_ZNK7simdutf7haswell14implementation24convert_utf32_to_utf16beEPKDimPDs:
 1007|    634|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1008|    634|  std::pair<const char32_t *, char16_t *> ret =
 1009|    634|      avx2_convert_utf32_to_utf16<endianness::BIG>(buf, len, utf16_output);
 1010|    634|  if (ret.first == nullptr) {
  ------------------
  |  Branch (1010:7): [True: 220, False: 414]
  ------------------
 1011|    220|    return 0;
 1012|    220|  }
 1013|    414|  size_t saved_bytes = ret.second - utf16_output;
 1014|    414|  if (ret.first != buf + len) {
  ------------------
  |  Branch (1014:7): [True: 412, False: 2]
  ------------------
 1015|    412|    const size_t scalar_saved_bytes =
 1016|    412|        scalar::utf32_to_utf16::convert<endianness::BIG>(
 1017|    412|            ret.first, len - (ret.first - buf), ret.second);
 1018|    412|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (1018:9): [True: 154, False: 258]
  ------------------
 1019|    154|      return 0;
 1020|    154|    }
 1021|    258|    saved_bytes += scalar_saved_bytes;
 1022|    258|  }
 1023|    260|  return saved_bytes;
 1024|    414|}
_ZNK7simdutf7haswell14implementation36convert_utf32_to_utf16le_with_errorsEPKDimPDs:
 1027|    658|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1028|       |  // ret.first.count is always the position in the buffer, not the number of
 1029|       |  // code units written even if finished
 1030|    658|  std::pair<result, char16_t *> ret =
 1031|    658|      haswell::avx2_convert_utf32_to_utf16_with_errors<endianness::LITTLE>(
 1032|    658|          buf, len, utf16_output);
 1033|    658|  if (ret.first.count != len) {
  ------------------
  |  Branch (1033:7): [True: 654, False: 4]
  ------------------
 1034|    654|    result scalar_res =
 1035|    654|        scalar::utf32_to_utf16::convert_with_errors<endianness::LITTLE>(
 1036|    654|            buf + ret.first.count, len - ret.first.count, ret.second);
 1037|    654|    if (scalar_res.error) {
  ------------------
  |  Branch (1037:9): [True: 482, False: 172]
  ------------------
 1038|    482|      scalar_res.count += ret.first.count;
 1039|    482|      return scalar_res;
 1040|    482|    } else {
 1041|    172|      ret.second += scalar_res.count;
 1042|    172|    }
 1043|    654|  }
 1044|    176|  ret.first.count =
 1045|    176|      ret.second -
 1046|    176|      utf16_output; // Set count to the number of 8-bit code units written
 1047|    176|  return ret.first;
 1048|    658|}
_ZNK7simdutf7haswell14implementation36convert_utf32_to_utf16be_with_errorsEPKDimPDs:
 1051|    592|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1052|       |  // ret.first.count is always the position in the buffer, not the number of
 1053|       |  // code units written even if finished
 1054|    592|  std::pair<result, char16_t *> ret =
 1055|    592|      haswell::avx2_convert_utf32_to_utf16_with_errors<endianness::BIG>(
 1056|    592|          buf, len, utf16_output);
 1057|    592|  if (ret.first.count != len) {
  ------------------
  |  Branch (1057:7): [True: 588, False: 4]
  ------------------
 1058|    588|    result scalar_res =
 1059|    588|        scalar::utf32_to_utf16::convert_with_errors<endianness::BIG>(
 1060|    588|            buf + ret.first.count, len - ret.first.count, ret.second);
 1061|    588|    if (scalar_res.error) {
  ------------------
  |  Branch (1061:9): [True: 414, False: 174]
  ------------------
 1062|    414|      scalar_res.count += ret.first.count;
 1063|    414|      return scalar_res;
 1064|    414|    } else {
 1065|    174|      ret.second += scalar_res.count;
 1066|    174|    }
 1067|    588|  }
 1068|    178|  ret.first.count =
 1069|    178|      ret.second -
 1070|    178|      utf16_output; // Set count to the number of 8-bit code units written
 1071|    178|  return ret.first;
 1072|    592|}
_ZNK7simdutf7haswell14implementation30convert_valid_utf32_to_utf16leEPKDimPDs:
 1075|    242|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1076|    242|  return convert_utf32_to_utf16le(buf, len, utf16_output);
 1077|    242|}
_ZNK7simdutf7haswell14implementation30convert_valid_utf32_to_utf16beEPKDimPDs:
 1080|    126|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1081|    126|  return convert_utf32_to_utf16be(buf, len, utf16_output);
 1082|    126|}
_ZNK7simdutf7haswell14implementation30convert_valid_utf16le_to_utf32EPKDsmPDi:
 1085|    120|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
 1086|    120|  return convert_utf16le_to_utf32(buf, len, utf32_output);
 1087|    120|}
_ZNK7simdutf7haswell14implementation30convert_valid_utf16be_to_utf32EPKDsmPDi:
 1090|    146|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
 1091|    146|  return convert_utf16be_to_utf32(buf, len, utf32_output);
 1092|    146|}
_ZNK7simdutf7haswell14implementation13count_utf16leEPKDsm:
 1103|  1.39k|    const char16_t *input, size_t length) const noexcept {
 1104|  1.39k|  return utf16::count_code_points<endianness::LITTLE>(input, length);
 1105|  1.39k|}
_ZNK7simdutf7haswell14implementation13count_utf16beEPKDsm:
 1108|  1.30k|    const char16_t *input, size_t length) const noexcept {
 1109|  1.30k|  return utf16::count_code_points<endianness::BIG>(input, length);
 1110|  1.30k|}
_ZNK7simdutf7haswell14implementation10count_utf8EPKcm:
 1115|  3.96k|implementation::count_utf8(const char *in, size_t size) const noexcept {
 1116|  3.96k|  return utf8::count_code_points_bytemask(in, size);
 1117|  3.96k|}
_ZNK7simdutf7haswell14implementation23latin1_length_from_utf8EPKcm:
 1122|    538|    const char *buf, size_t len) const noexcept {
 1123|    538|  return count_utf8(buf, len);
 1124|    538|}
_ZNK7simdutf7haswell14implementation24utf8_length_from_utf16leEPKDsm:
 1129|    767|    const char16_t *input, size_t length) const noexcept {
 1130|    767|  return utf16::utf8_length_from_utf16_bytemask<endianness::LITTLE>(input,
 1131|    767|                                                                    length);
 1132|    767|}
_ZNK7simdutf7haswell14implementation24utf8_length_from_utf16beEPKDsm:
 1135|    741|    const char16_t *input, size_t length) const noexcept {
 1136|    741|  return utf16::utf8_length_from_utf16_bytemask<endianness::BIG>(input, length);
 1137|    741|}
_ZNK7simdutf7haswell14implementation25utf32_length_from_utf16leEPKDsm:
 1142|    417|    const char16_t *input, size_t length) const noexcept {
 1143|    417|  return utf16::utf32_length_from_utf16<endianness::LITTLE>(input, length);
 1144|    417|}
_ZNK7simdutf7haswell14implementation25utf32_length_from_utf16beEPKDsm:
 1147|    389|    const char16_t *input, size_t length) const noexcept {
 1148|    389|  return utf16::utf32_length_from_utf16<endianness::BIG>(input, length);
 1149|    389|}
_ZNK7simdutf7haswell14implementation22utf16_length_from_utf8EPKcm:
 1154|  1.88k|    const char *input, size_t length) const noexcept {
 1155|  1.88k|  return utf8::utf16_length_from_utf8_bytemask(input, length);
 1156|  1.88k|}
_ZNK7simdutf7haswell14implementation23utf8_length_from_latin1EPKcm:
 1189|    307|    const char *input, size_t len) const noexcept {
 1190|    307|  const uint8_t *data = reinterpret_cast<const uint8_t *>(input);
 1191|    307|  size_t answer = len / sizeof(__m256i) * sizeof(__m256i);
 1192|    307|  size_t i = 0;
 1193|    307|  if (answer >= 2048) { // long strings optimization
  ------------------
  |  Branch (1193:7): [True: 157, False: 150]
  ------------------
 1194|    157|    __m256i four_64bits = _mm256_setzero_si256();
 1195|  3.18k|    while (i + sizeof(__m256i) <= len) {
  ------------------
  |  Branch (1195:12): [True: 3.02k, False: 157]
  ------------------
 1196|  3.02k|      __m256i runner = _mm256_setzero_si256();
 1197|       |      // We can do up to 255 loops without overflow.
 1198|  3.02k|      size_t iterations = (len - i) / sizeof(__m256i);
 1199|  3.02k|      if (iterations > 255) {
  ------------------
  |  Branch (1199:11): [True: 2.86k, False: 157]
  ------------------
 1200|  2.86k|        iterations = 255;
 1201|  2.86k|      }
 1202|  3.02k|      size_t max_i = i + iterations * sizeof(__m256i) - sizeof(__m256i);
 1203|   187k|      for (; i + 4 * sizeof(__m256i) <= max_i; i += 4 * sizeof(__m256i)) {
  ------------------
  |  Branch (1203:14): [True: 184k, False: 3.02k]
  ------------------
 1204|   184k|        __m256i input1 = _mm256_loadu_si256((const __m256i *)(data + i));
 1205|   184k|        __m256i input2 =
 1206|   184k|            _mm256_loadu_si256((const __m256i *)(data + i + sizeof(__m256i)));
 1207|   184k|        __m256i input3 = _mm256_loadu_si256(
 1208|   184k|            (const __m256i *)(data + i + 2 * sizeof(__m256i)));
 1209|   184k|        __m256i input4 = _mm256_loadu_si256(
 1210|   184k|            (const __m256i *)(data + i + 3 * sizeof(__m256i)));
 1211|   184k|        __m256i input12 =
 1212|   184k|            _mm256_add_epi8(_mm256_cmpgt_epi8(_mm256_setzero_si256(), input1),
 1213|   184k|                            _mm256_cmpgt_epi8(_mm256_setzero_si256(), input2));
 1214|   184k|        __m256i input23 =
 1215|   184k|            _mm256_add_epi8(_mm256_cmpgt_epi8(_mm256_setzero_si256(), input3),
 1216|   184k|                            _mm256_cmpgt_epi8(_mm256_setzero_si256(), input4));
 1217|   184k|        __m256i input1234 = _mm256_add_epi8(input12, input23);
 1218|   184k|        runner = _mm256_sub_epi8(runner, input1234);
 1219|   184k|      }
 1220|  12.0k|      for (; i <= max_i; i += sizeof(__m256i)) {
  ------------------
  |  Branch (1220:14): [True: 9.00k, False: 3.02k]
  ------------------
 1221|  9.00k|        __m256i input_256_chunk =
 1222|  9.00k|            _mm256_loadu_si256((const __m256i *)(data + i));
 1223|  9.00k|        runner = _mm256_sub_epi8(
 1224|  9.00k|            runner, _mm256_cmpgt_epi8(_mm256_setzero_si256(), input_256_chunk));
 1225|  9.00k|      }
 1226|  3.02k|      four_64bits = _mm256_add_epi64(
 1227|  3.02k|          four_64bits, _mm256_sad_epu8(runner, _mm256_setzero_si256()));
 1228|  3.02k|    }
 1229|    157|    answer += _mm256_extract_epi64(four_64bits, 0) +
 1230|    157|              _mm256_extract_epi64(four_64bits, 1) +
 1231|    157|              _mm256_extract_epi64(four_64bits, 2) +
 1232|    157|              _mm256_extract_epi64(four_64bits, 3);
 1233|    157|  } else if (answer > 0) {
  ------------------
  |  Branch (1233:14): [True: 80, False: 70]
  ------------------
 1234|  1.15k|    for (; i + sizeof(__m256i) <= len; i += sizeof(__m256i)) {
  ------------------
  |  Branch (1234:12): [True: 1.07k, False: 80]
  ------------------
 1235|  1.07k|      __m256i latin = _mm256_loadu_si256((const __m256i *)(data + i));
 1236|  1.07k|      uint32_t non_ascii = _mm256_movemask_epi8(latin);
 1237|  1.07k|      answer += count_ones(non_ascii);
 1238|  1.07k|    }
 1239|     80|  }
 1240|    307|  return answer + scalar::latin1::utf8_length_from_latin1(
 1241|    307|                      reinterpret_cast<const char *>(data + i), len - i);
 1242|    307|}
_ZNK7simdutf7haswell14implementation22utf8_length_from_utf32EPKDim:
 1247|    884|    const char32_t *input, size_t length) const noexcept {
 1248|    884|  return utf32::utf8_length_from_utf32(input, length);
 1249|    884|}
_ZNK7simdutf7haswell14implementation23utf16_length_from_utf32EPKDim:
 1254|  1.32k|    const char32_t *input, size_t length) const noexcept {
 1255|  1.32k|  const __m256i v_00000000 = _mm256_setzero_si256();
 1256|  1.32k|  const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000);
 1257|  1.32k|  size_t pos = 0;
 1258|  1.32k|  size_t count = 0;
 1259|   776k|  for (; pos + 8 <= length; pos += 8) {
  ------------------
  |  Branch (1259:10): [True: 775k, False: 1.32k]
  ------------------
 1260|   775k|    __m256i in = _mm256_loadu_si256((__m256i *)(input + pos));
 1261|   775k|    const __m256i surrogate_bytemask =
 1262|   775k|        _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffff0000), v_00000000);
 1263|   775k|    const uint32_t surrogate_bitmask =
 1264|   775k|        static_cast<uint32_t>(_mm256_movemask_epi8(surrogate_bytemask));
 1265|   775k|    size_t surrogate_count = (32 - count_ones(surrogate_bitmask)) / 4;
 1266|   775k|    count += 8 + surrogate_count;
 1267|   775k|  }
 1268|  1.32k|  return count +
 1269|  1.32k|         scalar::utf32::utf16_length_from_utf32(input + pos, length - pos);
 1270|  1.32k|}
_ZNK7simdutf7haswell14implementation22utf32_length_from_utf8EPKcm:
 1275|    995|    const char *input, size_t length) const noexcept {
 1276|    995|  return utf8::count_code_points(input, length);
 1277|    995|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_18is_asciiERKNS1_4simd8simd8x64IhEE:
   13|  4.98M|simdutf_really_inline bool is_ascii(const simd8x64<uint8_t> &input) {
   14|  4.98M|  return input.reduce_or().is_ascii();
   15|  4.98M|}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_124must_be_2_3_continuationENS1_4simd5simd8IhEES4_:
   22|  3.54M|                         const simd8<uint8_t> prev3) {
   23|  3.54M|  simd8<uint8_t> is_third_byte =
   24|  3.54M|      prev2.saturating_sub(0xe0u - 0x80); // Only 111_____ will be > 0x80
   25|  3.54M|  simd8<uint8_t> is_fourth_byte =
   26|  3.54M|      prev3.saturating_sub(0xf0u - 0x80); // Only 1111____ will be > 0x80
   27|  3.54M|  return simd8<bool>(is_third_byte | is_fourth_byte);
   28|  3.54M|}

_ZNK7simdutf14implementation27supported_by_runtime_systemEv:
   65|      8|bool implementation::supported_by_runtime_system() const {
   66|      8|  uint32_t required_instruction_sets = this->required_instruction_sets();
   67|      8|  uint32_t supported_instruction_sets =
   68|      8|      internal::detect_supported_architectures();
   69|      8|  return ((supported_instruction_sets & required_instruction_sets) ==
   70|      8|          required_instruction_sets);
   71|      8|}
_ZNK7simdutf8internal29available_implementation_list5beginEv:
 1504|      1|available_implementation_list::begin() const noexcept {
 1505|      1|  return internal::get_available_implementation_pointers().begin();
 1506|      1|}
_ZNK7simdutf8internal29available_implementation_list3endEv:
 1508|      1|available_implementation_list::end() const noexcept {
 1509|      1|  return internal::get_available_implementation_pointers().end();
 1510|      1|}
_ZN7simdutf29get_available_implementationsEv:
 1559|      1|get_available_implementations() {
 1560|      1|#if !SIMDUTF_USE_STATIC_INITIALIZATION
 1561|      1|  static const internal::available_implementation_list
 1562|      1|      available_implementations_instance{};
 1563|      1|#endif
 1564|      1|  return available_implementations_instance;
 1565|      1|}
simdutf.cpp:_ZN7simdutf8internalL37get_available_implementation_pointersEv:
  934|      2|get_available_implementation_pointers() {
  935|      2|#if !SIMDUTF_USE_STATIC_INITIALIZATION
  936|      2|  static const std::initializer_list<const implementation *>
  937|      2|      available_implementation_pointers{
  938|      2|  #if SIMDUTF_IMPLEMENTATION_ICELAKE
  939|      2|          get_icelake_singleton(),
  940|      2|  #endif
  941|      2|  #if SIMDUTF_IMPLEMENTATION_HASWELL
  942|      2|          get_haswell_singleton(),
  943|      2|  #endif
  944|      2|  #if SIMDUTF_IMPLEMENTATION_WESTMERE
  945|      2|          get_westmere_singleton(),
  946|      2|  #endif
  947|       |  #if SIMDUTF_IMPLEMENTATION_ARM64
  948|       |          get_arm64_singleton(),
  949|       |  #endif
  950|       |  #if SIMDUTF_IMPLEMENTATION_PPC64
  951|       |          get_ppc64_singleton(),
  952|       |  #endif
  953|       |  #if SIMDUTF_IMPLEMENTATION_RVV
  954|       |          get_rvv_singleton(),
  955|       |  #endif
  956|       |  #if SIMDUTF_IMPLEMENTATION_LASX
  957|       |          get_lasx_singleton(),
  958|       |  #endif
  959|       |  #if SIMDUTF_IMPLEMENTATION_LSX
  960|       |          get_lsx_singleton(),
  961|       |  #endif
  962|      2|  #if SIMDUTF_IMPLEMENTATION_FALLBACK
  963|      2|          get_fallback_singleton(),
  964|      2|  #endif
  965|      2|      };
  966|      2|#endif
  967|      2|  return available_implementation_pointers;
  968|      2|}
simdutf.cpp:_ZN7simdutf8internalL21get_icelake_singletonEv:
  158|      1|static const icelake::implementation *get_icelake_singleton() {
  159|      1|  #if !SIMDUTF_USE_STATIC_INITIALIZATION
  160|      1|  static const icelake::implementation icelake_singleton{};
  161|      1|  #endif
  162|      1|  return &icelake_singleton;
  163|      1|}
simdutf.cpp:_ZN7simdutf8internalL21get_haswell_singletonEv:
  169|      1|static const haswell::implementation *get_haswell_singleton() {
  170|      1|  #if !SIMDUTF_USE_STATIC_INITIALIZATION
  171|      1|  static const haswell::implementation haswell_singleton{};
  172|      1|  #endif
  173|      1|  return &haswell_singleton;
  174|      1|}
simdutf.cpp:_ZN7simdutf8internalL22get_westmere_singletonEv:
  180|      1|static const westmere::implementation *get_westmere_singleton() {
  181|      1|  #if !SIMDUTF_USE_STATIC_INITIALIZATION
  182|      1|  static const westmere::implementation westmere_singleton{};
  183|      1|  #endif
  184|      1|  return &westmere_singleton;
  185|      1|}
simdutf.cpp:_ZN7simdutf8internalL22get_fallback_singletonEv:
  246|      1|static const fallback::implementation *get_fallback_singleton() {
  247|      1|  #if !SIMDUTF_USE_STATIC_INITIALIZATION
  248|      1|  static const fallback::implementation fallback_singleton{};
  249|      1|  #endif
  250|      1|  return &fallback_singleton;
  251|      1|}

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

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

_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|  20.5k|  simdutf_really_inline simd8() : base8_numeric<uint8_t>() {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhEC2Ev:
  124|  20.5k|  simdutf_really_inline base8_numeric() : base8<T>() {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEEC2Ev:
   71|  20.5k|  simdutf_really_inline base8() : base<simd8<T>>() {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEC2Ev:
   14|  20.5k|  simdutf_really_inline base() : value{__m256i()} {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IhE9reduce_orEv:
  294|  4.98M|  simdutf_really_inline simd8<T> reduce_or() const {
  295|  4.98M|    return this->chunks[0] | this->chunks[1];
  296|  4.98M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEorES5_:
   50|  16.2M|  simdutf_really_inline Child operator|(const Child other) const {
   51|  16.2M|    return _mm256_or_si256(*this, other);
   52|  16.2M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEC2EDv4_x:
  193|   175M|      : base8_numeric<uint8_t>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhEC2EDv4_x:
  126|   175M|      : base8<T>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEEC2EDv4_x:
   73|   175M|  simdutf_really_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEC2EDv4_x:
   17|   175M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE8is_asciiEv:
  234|  4.98M|  simdutf_really_inline bool is_ascii() const {
  235|  4.98M|    return _mm256_movemask_epi8(*this) == 0;
  236|  4.98M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEoRES5_:
   59|  7.74M|  simdutf_really_inline Child &operator|=(const Child other) {
   60|  7.74M|    auto this_cast = static_cast<Child *>(this);
   61|  7.74M|    *this_cast = *this_cast | other;
   62|  7.74M|    return *this_cast;
   63|  7.74M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi1EEENS4_IhEES8_:
   83|  3.67M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   84|       |    return _mm256_alignr_epi8(
   85|  3.67M|        *this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N);
   86|  3.67M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE3shrILi4EEES4_v:
  245|  7.35M|  template <int N> simdutf_really_inline simd8<uint8_t> shr() const {
  246|  7.35M|    return simd8<uint8_t>(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N);
  247|  7.35M|  }
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|  11.0M|            L replace15) const {
  158|  11.0M|    return lookup_16(simd8<L>::repeat_16(
  159|  11.0M|        replace0, replace1, replace2, replace3, replace4, replace5, replace6,
  160|  11.0M|        replace7, replace8, replace9, replace10, replace11, replace12,
  161|  11.0M|        replace13, replace14, replace15));
  162|  11.0M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE9lookup_16IhEENS2_5simd8IT_EES8_:
  148|  11.0M|  simdutf_really_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
  149|  11.0M|    return _mm256_shuffle_epi8(lookup_table, *this);
  150|  11.0M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE9repeat_16Ehhhhhhhhhhhhhhhh:
  118|  11.0M|                                                  T v14, T v15) {
  119|  11.0M|    return simd8<T>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
  120|  11.0M|                    v14, v15, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
  121|  11.0M|                    v12, v13, v14, v15);
  122|  11.0M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEC2Ehhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh:
  207|  11.0M|      : simd8(_mm256_setr_epi8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
  208|  11.0M|                               v12, v13, v14, v15, v16, v17, v18, v19, v20, v21,
  209|  11.0M|                               v22, v23, v24, v25, v26, v27, v28, v29, v30,
  210|  11.0M|                               v31)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEC2Eh:
  195|  25.6M|  simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi2EEENS4_IhEES8_:
   83|  3.54M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   84|       |    return _mm256_alignr_epi8(
   85|  3.54M|        *this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N);
   86|  3.54M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi3EEENS4_IhEES8_:
   83|  3.54M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   84|       |    return _mm256_alignr_epi8(
   85|  3.54M|        *this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N);
   86|  3.54M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE14saturating_subES4_:
  214|  8.00M|  saturating_sub(const simd8<uint8_t> other) const {
  215|  8.00M|    return _mm256_subs_epu8(*this, other);
  216|  8.00M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IbEC2EDv4_x:
   95|  25.9M|  simdutf_really_inline simd8(const __m256i _value) : base8<bool>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5base8IbNS2_5simd8IbEEEC2EDv4_x:
   73|  25.9M|  simdutf_really_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IbEEEC2EDv4_x:
   17|  25.9M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IbEEEcvRKDv4_xEv:
   19|  16.5M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEeoES5_:
   56|  3.54M|  simdutf_really_inline Child operator^(const Child other) const {
   57|  3.54M|    return _mm256_xor_si256(*this, other);
   58|  3.54M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEC2EPKh:
  197|   920k|  simdutf_really_inline simd8(const uint8_t values[32]) : simd8(load(values)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE4loadEPKh:
  111|  10.8M|  static simdutf_really_inline simd8<T> load(const T values[32]) {
  112|  10.8M|    return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(values));
  113|  10.8M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE7gt_bitsES4_:
  225|   920k|  gt_bits(const simd8<uint8_t> other) const {
  226|   920k|    return this->saturating_sub(other);
  227|   920k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEC2EDv4_x:
   17|  74.8M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEcvRKDv4_xEv:
   19|   105M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IbE10to_bitmaskEv:
   99|  9.35M|  simdutf_really_inline uint32_t to_bitmask() const {
  100|  9.35M|    return uint32_t(_mm256_movemask_epi8(value));
  101|  9.35M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE21any_bits_set_anywhereEv:
  241|  2.38M|  simdutf_really_inline bool any_bits_set_anywhere() const {
  242|  2.38M|    return !bits_not_set_anywhere();
  243|  2.38M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE21bits_not_set_anywhereEv:
  237|  2.38M|  simdutf_really_inline bool bits_not_set_anywhere() const {
  238|  2.38M|    return _mm256_testz_si256(*this, *this);
  239|  2.38M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaEC2EPKa:
  274|  6.31M|      : chunks{simd8<T>::load(ptr),
  275|  6.31M|               simd8<T>::load(ptr + sizeof(simd8<T>) / sizeof(T))} {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIaE4loadEPKa:
  111|  22.6M|  static simdutf_really_inline simd8<T> load(const T values[32]) {
  112|  22.6M|    return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(values));
  113|  22.6M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IaEC2EDv4_x:
  169|  40.1M|      : base8_numeric<int8_t>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIaEC2EDv4_x:
  126|  40.1M|      : base8<T>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5base8IaNS2_5simd8IbEEEC2EDv4_x:
   73|  40.1M|  simdutf_really_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IaEEEC2EDv4_x:
   17|  40.1M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE8is_asciiEv:
  298|  5.50M|  simdutf_really_inline bool is_ascii() const {
  299|  5.50M|    return this->reduce_or().is_ascii();
  300|  5.50M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE9reduce_orEv:
  294|  5.50M|  simdutf_really_inline simd8<T> reduce_or() const {
  295|  5.50M|    return this->chunks[0] | this->chunks[1];
  296|  5.50M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IaEEEorES5_:
   50|  5.50M|  simdutf_really_inline Child operator|(const Child other) const {
   51|  5.50M|    return _mm256_or_si256(*this, other);
   52|  5.50M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IaEEEcvRKDv4_xEv:
   19|  64.6M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IaE8is_asciiEv:
  177|  5.50M|  simdutf_really_inline bool is_ascii() const {
  178|  5.50M|    return _mm256_movemask_epi8(*this) == 0;
  179|  5.50M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE5storeEPa:
  277|   950k|  simdutf_really_inline void store(T *ptr) const {
  278|   950k|    this->chunks[0].store(ptr + sizeof(simd8<T>) * 0 / sizeof(T));
  279|   950k|    this->chunks[1].store(ptr + sizeof(simd8<T>) * 1 / sizeof(T));
  280|   950k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIaE5storeEPa:
  129|  1.90M|  simdutf_really_inline void store(T dst[32]) const {
  130|  1.90M|    return _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), *this);
  131|  1.90M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IaEcvNS3_IhEEEv:
  256|  2.75M|simdutf_really_inline simd8<int8_t>::operator simd8<uint8_t>() const {
  257|  2.75M|  return this->value;
  258|  2.75M|}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE2ltEa:
  325|  1.19M|  simdutf_really_inline uint64_t lt(const T m) const {
  326|  1.19M|    const simd8<T> mask = simd8<T>::splat(m);
  327|  1.19M|    return simd8x64<bool>(this->chunks[0] < mask, this->chunks[1] < mask)
  328|  1.19M|        .to_bitmask();
  329|  1.19M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIaE5splatEa:
  105|  12.0M|  static simdutf_really_inline simd8<T> splat(T _value) {
  106|  12.0M|    return _mm256_set1_epi8(_value);
  107|  12.0M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IaEltES4_:
  184|  2.39M|  simdutf_really_inline simd8<bool> operator<(const simd8<int8_t> other) const {
  185|  2.39M|    return _mm256_cmpgt_epi8(other, *this);
  186|  2.39M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IbEC2ENS2_5simd8IbEES6_:
  272|  2.01M|      : chunks{chunk0, chunk1} {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IbE10to_bitmaskEv:
  282|  2.01M|  simdutf_really_inline uint64_t to_bitmask() const {
  283|  2.01M|    uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask());
  284|  2.01M|    uint64_t r_hi = this->chunks[1].to_bitmask();
  285|  2.01M|    return r_lo | (r_hi << 32);
  286|  2.01M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE20store_ascii_as_utf32EPDi:
  310|  1.18M|  simdutf_really_inline void store_ascii_as_utf32(char32_t *ptr) const {
  311|  1.18M|    this->chunks[0].store_ascii_as_utf32(ptr + sizeof(simd8<T>) * 0);
  312|  1.18M|    this->chunks[1].store_ascii_as_utf32(ptr + sizeof(simd8<T>) * 1);
  313|  1.18M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IaEEE20store_ascii_as_utf32EPDi:
   36|  2.37M|  simdutf_really_inline void store_ascii_as_utf32(char32_t *ptr) const {
   37|  2.37M|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr),
   38|  2.37M|                        _mm256_cvtepu8_epi32(_mm256_castsi256_si128(*this)));
   39|  2.37M|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr + 8),
   40|  2.37M|                        _mm256_cvtepu8_epi32(_mm256_castsi256_si128(
   41|  2.37M|                            _mm256_srli_si256(*this, 8))));
   42|  2.37M|    _mm256_storeu_si256(
   43|  2.37M|        reinterpret_cast<__m256i *>(ptr + 16),
   44|  2.37M|        _mm256_cvtepu8_epi32(_mm256_extractf128_si256(*this, 1)));
   45|  2.37M|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr + 24),
   46|       |                        _mm256_cvtepu8_epi32(_mm_srli_si128(
   47|  2.37M|                            _mm256_extractf128_si256(*this, 1), 8)));
   48|  2.37M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE4zeroEv:
  108|   107k|  static simdutf_really_inline simd8<T> zero() {
  109|   107k|    return _mm256_setzero_si256();
  110|   107k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IaEgtES4_:
  181|  11.6M|  simdutf_really_inline simd8<bool> operator>(const simd8<int8_t> other) const {
  182|  11.6M|    return _mm256_cmpgt_epi8(*this, other);
  183|  11.6M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd5simd8IaEC2Ea:
  172|  9.98M|  simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {}
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhEmIENS2_5simd8IhEE:
  137|  13.0M|  simdutf_really_inline simd8<T> &operator-=(const simd8<T> other) {
  138|  13.0M|    *this = *this - other;
  139|  13.0M|    return *static_cast<simd8<T> *>(this);
  140|  13.0M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhEmiENS2_5simd8IhEE:
  134|  13.0M|  simdutf_really_inline simd8<T> operator-(const simd8<T> other) const {
  135|  13.0M|    return _mm256_sub_epi8(*this, other);
  136|  13.0M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd10sum_8bytesENS2_5simd8IhEE:
  353|  51.0k|simdutf_really_inline simd64<uint64_t> sum_8bytes(const simd8<uint8_t> v) {
  354|  51.0k|  return _mm256_sad_epu8(v.value, simd8<uint8_t>::zero());
  355|  51.0k|}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE9sum_bytesEv:
  249|  2.74k|  simdutf_really_inline uint64_t sum_bytes() const {
  250|  2.74k|    const auto tmp = _mm256_sad_epu8(value, _mm256_setzero_si256());
  251|       |
  252|  2.74k|    return _mm256_extract_epi64(tmp, 0) + _mm256_extract_epi64(tmp, 1) +
  253|  2.74k|           _mm256_extract_epi64(tmp, 2) + _mm256_extract_epi64(tmp, 3);
  254|  2.74k|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhEgeES4_:
  229|  3.02M|  operator>=(const simd8<uint8_t> other) const {
  230|  3.02M|    return other.min_val(*this) == other;
  231|  3.02M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd5simd8IhE7min_valES4_:
  220|  3.02M|  min_val(const simd8<uint8_t> other) const {
  221|  3.02M|    return _mm256_min_epu8(other, *this);
  222|  3.02M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE2gtEa:
  331|   815k|  simdutf_really_inline uint64_t gt(const T m) const {
  332|   815k|    const simd8<T> mask = simd8<T>::splat(m);
  333|   815k|    return simd8x64<bool>(this->chunks[0] > mask, this->chunks[1] > mask)
  334|   815k|        .to_bitmask();
  335|   815k|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEC2EDv4_x:
   17|  15.1M|  simdutf_really_inline base(const __m256i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEcvRKDv4_xEv:
   19|  15.1M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd13base8_numericIhE5splatEh:
  105|  25.6M|  static simdutf_really_inline simd8<T> splat(T _value) {
  106|  25.6M|    return _mm256_set1_epi8(_value);
  107|  25.6M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEcvRKDv4_xEv:
   19|   233M|  simdutf_really_inline operator const __m256i &() const { return this->value; }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simdeqENS2_5simd8IhEES4_:
   76|  8.35M|                                               const simd8<T> rhs) {
   77|  8.35M|    return _mm256_cmpeq_epi8(lhs, rhs);
   78|  8.35M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEanES5_:
   53|  27.2M|  simdutf_really_inline Child operator&(const Child other) const {
   54|  27.2M|    return _mm256_and_si256(*this, other);
   55|  27.2M|  }
simdutf.cpp:_ZN7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IhEC2EPKh:
  274|  4.98M|      : chunks{simd8<T>::load(ptr),
  275|  4.98M|               simd8<T>::load(ptr + sizeof(simd8<T>) / sizeof(T))} {}
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEanES5_:
   53|  11.9M|  simdutf_really_inline Child operator&(const Child other) const {
   54|  11.9M|    return _mm256_and_si256(*this, other);
   55|  11.9M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE20store_ascii_as_utf16ILNS_10endiannessE0EEEvPDs:
  303|  1.03M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const {
  304|  1.03M|    this->chunks[0].template store_ascii_as_utf16<endian>(ptr +
  305|  1.03M|                                                          sizeof(simd8<T>) * 0);
  306|  1.03M|    this->chunks[1].template store_ascii_as_utf16<endian>(ptr +
  307|  1.03M|                                                          sizeof(simd8<T>) * 1);
  308|  1.03M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IaEEE20store_ascii_as_utf16ILNS_10endiannessE0EEEvPDs:
   22|  2.07M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const {
   23|  2.07M|    __m256i first = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(*this));
   24|  2.07M|    __m256i second = _mm256_cvtepu8_epi16(_mm256_extractf128_si256(*this, 1));
   25|  2.07M|    if (big_endian) {
  ------------------
  |  Branch (25:9): [Folded, False: 2.07M]
  ------------------
   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.07M|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr), first);
   33|  2.07M|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr + 16), second);
   34|  2.07M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd8simd8x64IaE20store_ascii_as_utf16ILNS_10endiannessE1EEEvPDs:
  303|  1.13M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const {
  304|  1.13M|    this->chunks[0].template store_ascii_as_utf16<endian>(ptr +
  305|  1.13M|                                                          sizeof(simd8<T>) * 0);
  306|  1.13M|    this->chunks[1].template store_ascii_as_utf16<endian>(ptr +
  307|  1.13M|                                                          sizeof(simd8<T>) * 1);
  308|  1.13M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_5simd8IaEEE20store_ascii_as_utf16ILNS_10endiannessE1EEEvPDs:
   22|  2.26M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const {
   23|  2.26M|    __m256i first = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(*this));
   24|  2.26M|    __m256i second = _mm256_cvtepu8_epi16(_mm256_extractf128_si256(*this, 1));
   25|  2.26M|    if (big_endian) {
  ------------------
  |  Branch (25:9): [True: 2.26M, Folded]
  ------------------
   26|  2.26M|      const __m256i swap = _mm256_setr_epi8(
   27|  2.26M|          1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
   28|  2.26M|          21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
   29|  2.26M|      first = _mm256_shuffle_epi8(first, swap);
   30|  2.26M|      second = _mm256_shuffle_epi8(second, swap);
   31|  2.26M|    }
   32|  2.26M|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr), first);
   33|  2.26M|    _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr + 16), second);
   34|  2.26M|  }
simdutf.cpp:_ZNK7simdutf7haswell12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEorES5_:
   50|  4.30M|  simdutf_really_inline Child operator|(const Child other) const {
   51|  4.30M|    return _mm256_or_si256(*this, other);
   52|  4.30M|  }

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

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

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

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

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

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2Ev:
  183|  20.5k|  simdutf_really_inline simd8() : base8_numeric<uint8_t>() {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhEC2Ev:
  118|  20.5k|  simdutf_really_inline base8_numeric() : base8<T>() {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEEC2Ev:
   68|  20.5k|  simdutf_really_inline base8() : base<simd8<T>>() {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEC2Ev:
   13|  20.5k|  simdutf_really_inline base() : value{__m128i()} {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IhE9reduce_orEv:
  290|  4.98M|  simdutf_really_inline simd8<T> reduce_or() const {
  291|  4.98M|    return (this->chunks[0] | this->chunks[1]) |
  292|  4.98M|           (this->chunks[2] | this->chunks[3]);
  293|  4.98M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEorES5_:
   42|  33.4M|  simdutf_really_inline Child operator|(const Child other) const {
   43|  33.4M|    return _mm_or_si128(*this, other);
   44|  33.4M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2EDv2_x:
  185|   339M|      : base8_numeric<uint8_t>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhEC2EDv2_x:
  120|   339M|      : base8<T>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEEC2EDv2_x:
   69|   339M|  simdutf_really_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEC2EDv2_x:
   16|   339M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE8is_asciiEv:
  228|  4.98M|  simdutf_really_inline bool is_ascii() const {
  229|  4.98M|    return _mm_movemask_epi8(*this) == 0;
  230|  4.98M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEoRES5_:
   51|  11.4M|  simdutf_really_inline Child &operator|=(const Child other) {
   52|  11.4M|    auto this_cast = static_cast<Child *>(this);
   53|  11.4M|    *this_cast = *this_cast | other;
   54|  11.4M|    return *this_cast;
   55|  11.4M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi1EEENS4_IhEES8_:
   79|  7.35M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   80|       |    return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
   81|  7.35M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE3shrILi4EEES4_v:
  238|  14.7M|  template <int N> simdutf_really_inline simd8<uint8_t> shr() const {
  239|  14.7M|    return simd8<uint8_t>(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N);
  240|  14.7M|  }
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|  22.0M|            L replace15) const {
  152|  22.0M|    return lookup_16(simd8<L>::repeat_16(
  153|  22.0M|        replace0, replace1, replace2, replace3, replace4, replace5, replace6,
  154|  22.0M|        replace7, replace8, replace9, replace10, replace11, replace12,
  155|  22.0M|        replace13, replace14, replace15));
  156|  22.0M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE9lookup_16IhEENS2_5simd8IT_EES8_:
  142|  22.0M|  simdutf_really_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
  143|  22.0M|    return _mm_shuffle_epi8(lookup_table, *this);
  144|  22.0M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE9repeat_16Ehhhhhhhhhhhhhhhh:
  113|  22.0M|                                                  T v14, T v15) {
  114|  22.0M|    return simd8<T>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
  115|  22.0M|                    v14, v15);
  116|  22.0M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2Ehhhhhhhhhhhhhhhh:
  196|  22.0M|      : simd8(_mm_setr_epi8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
  197|  22.0M|                            v12, v13, v14, v15)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2Eh:
  188|  50.2M|  simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi2EEENS4_IhEES8_:
   79|  7.08M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   80|       |    return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
   81|  7.08M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5base8IhNS2_5simd8IbEEE4prevILi3EEENS4_IhEES8_:
   79|  7.08M|  simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
   80|       |    return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
   81|  7.08M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE14saturating_subES4_:
  201|  15.0M|  saturating_sub(const simd8<uint8_t> other) const {
  202|  15.0M|    return _mm_subs_epu8(*this, other);
  203|  15.0M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IbEC2EDv2_x:
   91|  46.5M|  simdutf_really_inline simd8(const __m128i _value) : base8<bool>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5base8IbNS2_5simd8IbEEEC2EDv2_x:
   69|  46.5M|  simdutf_really_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IbEEEC2EDv2_x:
   16|  46.5M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IbEEEcvRKDv2_xEv:
   18|  46.5M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEeoES5_:
   48|  7.08M|  simdutf_really_inline Child operator^(const Child other) const {
   49|  7.08M|    return _mm_xor_si128(*this, other);
   50|  7.08M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEC2EPKh:
  190|   920k|  simdutf_really_inline simd8(const uint8_t *values) : simd8(load(values)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE4loadEPKh:
  106|  20.8M|  static simdutf_really_inline simd8<T> load(const T values[16]) {
  107|  20.8M|    return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
  108|  20.8M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE7gt_bitsES4_:
  212|   920k|  gt_bits(const simd8<uint8_t> other) const {
  213|   920k|    return this->saturating_sub(other);
  214|   920k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEC2EDv2_x:
   16|   121M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEcvRKDv2_xEv:
   18|   163M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IbE10to_bitmaskEv:
   95|  13.3M|  simdutf_really_inline int to_bitmask() const {
   96|  13.3M|    return _mm_movemask_epi8(*this);
   97|  13.3M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE21any_bits_set_anywhereEv:
  235|  2.38M|  simdutf_really_inline bool any_bits_set_anywhere() const {
  236|  2.38M|    return !bits_not_set_anywhere();
  237|  2.38M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE21bits_not_set_anywhereEv:
  232|  2.38M|  simdutf_really_inline bool bits_not_set_anywhere() const {
  233|  2.38M|    return _mm_testz_si128(*this, *this);
  234|  2.38M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaEC2EPKa:
  270|  6.31M|      : chunks{simd8<T>::load(ptr),
  271|  6.31M|               simd8<T>::load(ptr + sizeof(simd8<T>) / sizeof(T)),
  272|  6.31M|               simd8<T>::load(ptr + 2 * sizeof(simd8<T>) / sizeof(T)),
  273|  6.31M|               simd8<T>::load(ptr + 3 * sizeof(simd8<T>) / sizeof(T))} {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIaE4loadEPKa:
  106|  45.2M|  static simdutf_really_inline simd8<T> load(const T values[16]) {
  107|  45.2M|    return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
  108|  45.2M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IaEC2EDv2_x:
  163|  83.7M|      : base8_numeric<int8_t>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIaEC2EDv2_x:
  120|  83.7M|      : base8<T>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5base8IaNS2_5simd8IbEEEC2EDv2_x:
   69|  83.7M|  simdutf_really_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IaEEEC2EDv2_x:
   16|  83.7M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE8is_asciiEv:
  295|  5.50M|  simdutf_really_inline bool is_ascii() const {
  296|  5.50M|    return this->reduce_or().is_ascii();
  297|  5.50M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE9reduce_orEv:
  290|  5.50M|  simdutf_really_inline simd8<T> reduce_or() const {
  291|  5.50M|    return (this->chunks[0] | this->chunks[1]) |
  292|  5.50M|           (this->chunks[2] | this->chunks[3]);
  293|  5.50M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IaEEEorES5_:
   42|  16.5M|  simdutf_really_inline Child operator|(const Child other) const {
   43|  16.5M|    return _mm_or_si128(*this, other);
   44|  16.5M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IaEEEcvRKDv2_xEv:
   18|   134M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IaE8is_asciiEv:
  168|  5.50M|  simdutf_really_inline bool is_ascii() const {
  169|  5.50M|    return _mm_movemask_epi8(*this) == 0;
  170|  5.50M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE5storeEPa:
  275|   950k|  simdutf_really_inline void store(T *ptr) const {
  276|   950k|    this->chunks[0].store(ptr + sizeof(simd8<T>) * 0 / sizeof(T));
  277|   950k|    this->chunks[1].store(ptr + sizeof(simd8<T>) * 1 / sizeof(T));
  278|   950k|    this->chunks[2].store(ptr + sizeof(simd8<T>) * 2 / sizeof(T));
  279|   950k|    this->chunks[3].store(ptr + sizeof(simd8<T>) * 3 / sizeof(T));
  280|   950k|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIaE5storeEPa:
  123|  3.80M|  simdutf_really_inline void store(T dst[16]) const {
  124|  3.80M|    return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this);
  125|  3.80M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IaEcvNS3_IhEEEv:
  251|  6.43M|simdutf_really_inline simd8<int8_t>::operator simd8<uint8_t>() const {
  252|  6.43M|  return this->value;
  253|  6.43M|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE2ltEa:
  326|  1.19M|  simdutf_really_inline uint64_t lt(const T m) const {
  327|  1.19M|    const simd8<T> mask = simd8<T>::splat(m);
  328|  1.19M|    return simd8x64<bool>(this->chunks[0] < mask, this->chunks[1] < mask,
  329|  1.19M|                          this->chunks[2] < mask, this->chunks[3] < mask)
  330|  1.19M|        .to_bitmask();
  331|  1.19M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIaE5splatEa:
  102|  21.9M|  static simdutf_really_inline simd8<T> splat(T _value) {
  103|  21.9M|    return _mm_set1_epi8(_value);
  104|  21.9M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IaEltES4_:
  176|  4.79M|  simdutf_really_inline simd8<bool> operator<(const simd8<int8_t> other) const {
  177|  4.79M|    return _mm_cmpgt_epi8(other, *this);
  178|  4.79M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IbEC2ENS2_5simd8IbEES6_S6_S6_:
  268|  2.01M|      : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IbE10to_bitmaskEv:
  318|  2.01M|  simdutf_really_inline uint64_t to_bitmask() const {
  319|  2.01M|    uint64_t r0 = uint32_t(this->chunks[0].to_bitmask());
  320|  2.01M|    uint64_t r1 = this->chunks[1].to_bitmask();
  321|  2.01M|    uint64_t r2 = this->chunks[2].to_bitmask();
  322|  2.01M|    uint64_t r3 = this->chunks[3].to_bitmask();
  323|  2.01M|    return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
  324|  2.01M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE20store_ascii_as_utf32EPDi:
  311|  1.18M|  simdutf_really_inline void store_ascii_as_utf32(char32_t *ptr) const {
  312|  1.18M|    this->chunks[0].store_ascii_as_utf32(ptr + sizeof(simd8<T>) * 0);
  313|  1.18M|    this->chunks[1].store_ascii_as_utf32(ptr + sizeof(simd8<T>) * 1);
  314|  1.18M|    this->chunks[2].store_ascii_as_utf32(ptr + sizeof(simd8<T>) * 2);
  315|  1.18M|    this->chunks[3].store_ascii_as_utf32(ptr + sizeof(simd8<T>) * 3);
  316|  1.18M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IaEEE20store_ascii_as_utf32EPDi:
   32|  4.75M|  simdutf_really_inline void store_ascii_as_utf32(char32_t *p) const {
   33|  4.75M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p), _mm_cvtepu8_epi32(*this));
   34|  4.75M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p + 4),
   35|  4.75M|                     _mm_cvtepu8_epi32(_mm_srli_si128(*this, 4)));
   36|  4.75M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p + 8),
   37|  4.75M|                     _mm_cvtepu8_epi32(_mm_srli_si128(*this, 8)));
   38|  4.75M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p + 12),
   39|       |                     _mm_cvtepu8_epi32(_mm_srli_si128(*this, 12)));
   40|  4.75M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE4zeroEv:
  105|   210k|  static simdutf_really_inline simd8<T> zero() { return _mm_setzero_si128(); }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IaEgtES4_:
  173|  23.2M|  simdutf_really_inline simd8<bool> operator>(const simd8<int8_t> other) const {
  174|  23.2M|    return _mm_cmpgt_epi8(*this, other);
  175|  23.2M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd5simd8IaEC2Ea:
  165|  19.9M|  simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhEmIENS2_5simd8IhEE:
  134|  26.0M|  simdutf_really_inline simd8<T> &operator-=(const simd8<T> other) {
  135|  26.0M|    *this = *this - other;
  136|  26.0M|    return *static_cast<simd8<T> *>(this);
  137|  26.0M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhEmiENS2_5simd8IhEE:
  131|  26.0M|  simdutf_really_inline simd8<T> operator-(const simd8<T> other) const {
  132|  26.0M|    return _mm_sub_epi8(*this, other);
  133|  26.0M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd10sum_8bytesENS2_5simd8IhEE:
  366|   102k|simdutf_really_inline simd64<uint64_t> sum_8bytes(const simd8<uint8_t> v) {
  367|   102k|  return _mm_sad_epu8(v.value, simd8<uint8_t>::zero());
  368|   102k|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE9sum_bytesEv:
  245|  3.77k|  simdutf_really_inline uint64_t sum_bytes() const {
  246|  3.77k|    const auto tmp = _mm_sad_epu8(value, _mm_setzero_si128());
  247|  3.77k|    return _mm_extract_epi64(tmp, 0) + _mm_extract_epi64(tmp, 1);
  248|  3.77k|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhEgeES4_:
  217|  6.05M|  operator>=(const simd8<uint8_t> other) const {
  218|  6.05M|    return other.min_val(*this) == other;
  219|  6.05M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd5simd8IhE7min_valES4_:
  207|  6.05M|  min_val(const simd8<uint8_t> other) const {
  208|  6.05M|    return _mm_min_epu8(*this, other);
  209|  6.05M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE2gtEa:
  333|   815k|  simdutf_really_inline uint64_t gt(const T m) const {
  334|   815k|    const simd8<T> mask = simd8<T>::splat(m);
  335|   815k|    return simd8x64<bool>(this->chunks[0] > mask, this->chunks[1] > mask,
  336|   815k|                          this->chunks[2] > mask, this->chunks[3] > mask)
  337|   815k|        .to_bitmask();
  338|   815k|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEC2EDv2_x:
   16|  30.2M|  simdutf_really_inline base(const __m128i _value) : value(_value) {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEcvRKDv2_xEv:
   18|  30.2M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd13base8_numericIhE5splatEh:
  102|  50.3M|  static simdutf_really_inline simd8<T> splat(T _value) {
  103|  50.3M|    return _mm_set1_epi8(_value);
  104|  50.3M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEcvRKDv2_xEv:
   18|   415M|  simdutf_really_inline operator const __m128i &() const { return this->value; }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simdeqENS2_5simd8IhEES4_:
   72|  11.3M|                                               const simd8<T> rhs) {
   73|  11.3M|    return _mm_cmpeq_epi8(lhs, rhs);
   74|  11.3M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IhEEEanES5_:
   45|  49.1M|  simdutf_really_inline Child operator&(const Child other) const {
   46|  49.1M|    return _mm_and_si128(*this, other);
   47|  49.1M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IhEC2EPKh:
  270|  4.98M|      : chunks{simd8<T>::load(ptr),
  271|  4.98M|               simd8<T>::load(ptr + sizeof(simd8<T>) / sizeof(T)),
  272|  4.98M|               simd8<T>::load(ptr + 2 * sizeof(simd8<T>) / sizeof(T)),
  273|  4.98M|               simd8<T>::load(ptr + 3 * sizeof(simd8<T>) / sizeof(T))} {}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16ItEEEanES5_:
   45|  18.5M|  simdutf_really_inline Child operator&(const Child other) const {
   46|  18.5M|    return _mm_and_si128(*this, other);
   47|  18.5M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE20store_ascii_as_utf16ILNS_10endiannessE0EEEvPDs:
  300|  1.03M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const {
  301|  1.03M|    this->chunks[0].template store_ascii_as_utf16<endian>(ptr +
  302|  1.03M|                                                          sizeof(simd8<T>) * 0);
  303|  1.03M|    this->chunks[1].template store_ascii_as_utf16<endian>(ptr +
  304|  1.03M|                                                          sizeof(simd8<T>) * 1);
  305|  1.03M|    this->chunks[2].template store_ascii_as_utf16<endian>(ptr +
  306|  1.03M|                                                          sizeof(simd8<T>) * 2);
  307|  1.03M|    this->chunks[3].template store_ascii_as_utf16<endian>(ptr +
  308|  1.03M|                                                          sizeof(simd8<T>) * 3);
  309|  1.03M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IaEEE20store_ascii_as_utf16ILNS_10endiannessE0EEEvPDs:
   20|  4.14M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *p) const {
   21|  4.14M|    __m128i first = _mm_cvtepu8_epi16(*this);
   22|  4.14M|    __m128i second = _mm_cvtepu8_epi16(_mm_srli_si128(*this, 8));
   23|  4.14M|    if (big_endian) {
  ------------------
  |  Branch (23:9): [Folded, False: 4.14M]
  ------------------
   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.14M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p), first);
   30|  4.14M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p + 8), second);
   31|  4.14M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd8simd8x64IaE20store_ascii_as_utf16ILNS_10endiannessE1EEEvPDs:
  300|  1.13M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const {
  301|  1.13M|    this->chunks[0].template store_ascii_as_utf16<endian>(ptr +
  302|  1.13M|                                                          sizeof(simd8<T>) * 0);
  303|  1.13M|    this->chunks[1].template store_ascii_as_utf16<endian>(ptr +
  304|  1.13M|                                                          sizeof(simd8<T>) * 1);
  305|  1.13M|    this->chunks[2].template store_ascii_as_utf16<endian>(ptr +
  306|  1.13M|                                                          sizeof(simd8<T>) * 2);
  307|  1.13M|    this->chunks[3].template store_ascii_as_utf16<endian>(ptr +
  308|  1.13M|                                                          sizeof(simd8<T>) * 3);
  309|  1.13M|  }
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_5simd8IaEEE20store_ascii_as_utf16ILNS_10endiannessE1EEEvPDs:
   20|  4.52M|  simdutf_really_inline void store_ascii_as_utf16(char16_t *p) const {
   21|  4.52M|    __m128i first = _mm_cvtepu8_epi16(*this);
   22|  4.52M|    __m128i second = _mm_cvtepu8_epi16(_mm_srli_si128(*this, 8));
   23|  4.52M|    if (big_endian) {
  ------------------
  |  Branch (23:9): [True: 4.52M, Folded]
  ------------------
   24|  4.52M|      const __m128i swap =
   25|  4.52M|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   26|  4.52M|      first = _mm_shuffle_epi8(first, swap);
   27|  4.52M|      second = _mm_shuffle_epi8(second, swap);
   28|  4.52M|    }
   29|  4.52M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p), first);
   30|  4.52M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(p + 8), second);
   31|  4.52M|  }
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_14simd12as_vector_u8ENS2_6simd32IjEE:
  370|  9.73k|simdutf_really_inline simd8<uint8_t> as_vector_u8(const simd32<uint32_t> v) {
  371|  9.73k|  return simd8<uint8_t>(v.value);
  372|  9.73k|}
simdutf.cpp:_ZNK7simdutf8westmere12_GLOBAL__N_14simd4baseINS2_6simd16IbEEEorES5_:
   42|  8.60M|  simdutf_really_inline Child operator|(const Child other) const {
   43|  8.60M|    return _mm_or_si128(*this, other);
   44|  8.60M|  }

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

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

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

_ZNK7simdutf8westmere14implementation13validate_utf8EPKcm:
  297|  3.42k|implementation::validate_utf8(const char *buf, size_t len) const noexcept {
  298|  3.42k|  return westmere::utf8_validation::generic_validate_utf8(buf, len);
  299|  3.42k|}
_ZNK7simdutf8westmere14implementation25validate_utf8_with_errorsEPKcm:
  304|  3.42k|    const char *buf, size_t len) const noexcept {
  305|  3.42k|  return westmere::utf8_validation::generic_validate_utf8_with_errors(buf, len);
  306|  3.42k|}
_ZNK7simdutf8westmere14implementation16validate_utf16leEPKDsm:
  343|  1.39k|                                 size_t len) const noexcept {
  344|  1.39k|  if (simdutf_unlikely(len == 0)) {
  ------------------
  |  |   92|  1.39k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (92:33): [True: 16, False: 1.37k]
  |  |  ------------------
  ------------------
  345|       |    // empty input is valid UTF-16. protect the implementation from
  346|       |    // handling nullptr
  347|     16|    return true;
  348|     16|  }
  349|  1.37k|  const auto res =
  350|  1.37k|      westmere::utf16::validate_utf16_with_errors<endianness::LITTLE>(buf, len);
  351|  1.37k|  if (res.is_err()) {
  ------------------
  |  Branch (351:7): [True: 166, False: 1.20k]
  ------------------
  352|    166|    return false;
  353|    166|  }
  354|       |
  355|  1.20k|  if (res.count == len)
  ------------------
  |  Branch (355:7): [True: 0, False: 1.20k]
  ------------------
  356|      0|    return true;
  357|       |
  358|  1.20k|  return scalar::utf16::validate<endianness::LITTLE>(buf + res.count,
  359|  1.20k|                                                     len - res.count);
  360|  1.20k|}
_ZNK7simdutf8westmere14implementation16validate_utf16beEPKDsm:
  366|  1.30k|                                 size_t len) const noexcept {
  367|  1.30k|  if (simdutf_unlikely(len == 0)) {
  ------------------
  |  |   92|  1.30k|    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
  |  |  ------------------
  |  |  |  Branch (92:33): [True: 17, False: 1.28k]
  |  |  ------------------
  ------------------
  368|       |    // empty input is valid UTF-16. protect the implementation from
  369|       |    // handling nullptr
  370|     17|    return true;
  371|     17|  }
  372|  1.28k|  const auto res =
  373|  1.28k|      westmere::utf16::validate_utf16_with_errors<endianness::BIG>(buf, len);
  374|  1.28k|  if (res.is_err()) {
  ------------------
  |  Branch (374:7): [True: 127, False: 1.16k]
  ------------------
  375|    127|    return false;
  376|    127|  }
  377|       |
  378|  1.16k|  if (res.count == len)
  ------------------
  |  Branch (378:7): [True: 0, False: 1.16k]
  ------------------
  379|      0|    return true;
  380|       |
  381|  1.16k|  return scalar::utf16::validate<endianness::BIG>(buf + res.count,
  382|  1.16k|                                                  len - res.count);
  383|  1.16k|}
_ZNK7simdutf8westmere14implementation28validate_utf16le_with_errorsEPKDsm:
  386|  1.39k|    const char16_t *buf, size_t len) const noexcept {
  387|  1.39k|  const result res =
  388|  1.39k|      westmere::utf16::validate_utf16_with_errors<endianness::LITTLE>(buf, len);
  389|  1.39k|  if (res.count != len) {
  ------------------
  |  Branch (389:7): [True: 1.37k, False: 16]
  ------------------
  390|  1.37k|    const result scalar_res =
  391|  1.37k|        scalar::utf16::validate_with_errors<endianness::LITTLE>(
  392|  1.37k|            buf + res.count, len - res.count);
  393|  1.37k|    return result(scalar_res.error, res.count + scalar_res.count);
  394|  1.37k|  } else {
  395|     16|    return res;
  396|     16|  }
  397|  1.39k|}
_ZNK7simdutf8westmere14implementation28validate_utf16be_with_errorsEPKDsm:
  400|  1.30k|    const char16_t *buf, size_t len) const noexcept {
  401|  1.30k|  const result res =
  402|  1.30k|      westmere::utf16::validate_utf16_with_errors<endianness::BIG>(buf, len);
  403|  1.30k|  if (res.count != len) {
  ------------------
  |  Branch (403:7): [True: 1.28k, False: 17]
  ------------------
  404|  1.28k|    result scalar_res = scalar::utf16::validate_with_errors<endianness::BIG>(
  405|  1.28k|        buf + res.count, len - res.count);
  406|  1.28k|    return result(scalar_res.error, res.count + scalar_res.count);
  407|  1.28k|  } else {
  408|     17|    return res;
  409|     17|  }
  410|  1.30k|}
_ZNK7simdutf8westmere14implementation14validate_utf32EPKDim:
  425|  2.49k|implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept {
  426|  2.49k|  return utf32::validate(buf, len);
  427|  2.49k|}
_ZNK7simdutf8westmere14implementation26validate_utf32_with_errorsEPKDim:
  432|  2.49k|    const char32_t *buf, size_t len) const noexcept {
  433|  2.49k|  return utf32::validate_with_errors(buf, len);
  434|  2.49k|}
_ZNK7simdutf8westmere14implementation22convert_latin1_to_utf8EPKcmPc:
  439|    614|    const char *buf, size_t len, char *utf8_output) const noexcept {
  440|       |
  441|    614|  std::pair<const char *, char *> ret =
  442|    614|      sse_convert_latin1_to_utf8(buf, len, utf8_output);
  443|    614|  size_t converted_chars = ret.second - utf8_output;
  444|       |
  445|    614|  if (ret.first != buf + len) {
  ------------------
  |  Branch (445:7): [True: 490, False: 124]
  ------------------
  446|    490|    const size_t scalar_converted_chars = scalar::latin1_to_utf8::convert(
  447|    490|        ret.first, len - (ret.first - buf), ret.second);
  448|    490|    converted_chars += scalar_converted_chars;
  449|    490|  }
  450|       |
  451|    614|  return converted_chars;
  452|    614|}
_ZNK7simdutf8westmere14implementation25convert_latin1_to_utf16leEPKcmPDs:
  457|     88|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  458|     88|  std::pair<const char *, char16_t *> ret =
  459|     88|      sse_convert_latin1_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
  460|     88|  if (ret.first == nullptr) {
  ------------------
  |  Branch (460:7): [True: 4, False: 84]
  ------------------
  461|      4|    return 0;
  462|      4|  }
  463|     84|  size_t converted_chars = ret.second - utf16_output;
  464|     84|  if (ret.first != buf + len) {
  ------------------
  |  Branch (464:7): [True: 70, False: 14]
  ------------------
  465|     70|    const size_t scalar_converted_chars =
  466|     70|        scalar::latin1_to_utf16::convert<endianness::LITTLE>(
  467|     70|            ret.first, len - (ret.first - buf), ret.second);
  468|     70|    if (scalar_converted_chars == 0) {
  ------------------
  |  Branch (468:9): [True: 0, False: 70]
  ------------------
  469|      0|      return 0;
  470|      0|    }
  471|     70|    converted_chars += scalar_converted_chars;
  472|     70|  }
  473|     84|  return converted_chars;
  474|     84|}
_ZNK7simdutf8westmere14implementation25convert_latin1_to_utf16beEPKcmPDs:
  477|    100|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  478|    100|  std::pair<const char *, char16_t *> ret =
  479|    100|      sse_convert_latin1_to_utf16<endianness::BIG>(buf, len, utf16_output);
  480|    100|  if (ret.first == nullptr) {
  ------------------
  |  Branch (480:7): [True: 4, False: 96]
  ------------------
  481|      4|    return 0;
  482|      4|  }
  483|     96|  size_t converted_chars = ret.second - utf16_output;
  484|     96|  if (ret.first != buf + len) {
  ------------------
  |  Branch (484:7): [True: 80, False: 16]
  ------------------
  485|     80|    const size_t scalar_converted_chars =
  486|     80|        scalar::latin1_to_utf16::convert<endianness::BIG>(
  487|     80|            ret.first, len - (ret.first - buf), ret.second);
  488|     80|    if (scalar_converted_chars == 0) {
  ------------------
  |  Branch (488:9): [True: 0, False: 80]
  ------------------
  489|      0|      return 0;
  490|      0|    }
  491|     80|    converted_chars += scalar_converted_chars;
  492|     80|  }
  493|     96|  return converted_chars;
  494|     96|}
_ZNK7simdutf8westmere14implementation23convert_latin1_to_utf32EPKcmPDi:
  499|    126|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  500|    126|  std::pair<const char *, char32_t *> ret =
  501|    126|      sse_convert_latin1_to_utf32(buf, len, utf32_output);
  502|    126|  if (ret.first == nullptr) {
  ------------------
  |  Branch (502:7): [True: 4, False: 122]
  ------------------
  503|      4|    return 0;
  504|      4|  }
  505|    122|  size_t converted_chars = ret.second - utf32_output;
  506|    122|  if (ret.first != buf + len) {
  ------------------
  |  Branch (506:7): [True: 104, False: 18]
  ------------------
  507|    104|    const size_t scalar_converted_chars = scalar::latin1_to_utf32::convert(
  508|    104|        ret.first, len - (ret.first - buf), ret.second);
  509|    104|    if (scalar_converted_chars == 0) {
  ------------------
  |  Branch (509:9): [True: 0, False: 104]
  ------------------
  510|      0|      return 0;
  511|      0|    }
  512|    104|    converted_chars += scalar_converted_chars;
  513|    104|  }
  514|    122|  return converted_chars;
  515|    122|}
_ZNK7simdutf8westmere14implementation22convert_utf8_to_latin1EPKcmPc:
  520|    576|    const char *buf, size_t len, char *latin1_output) const noexcept {
  521|    576|  utf8_to_latin1::validating_transcoder converter;
  522|    576|  return converter.convert(buf, len, latin1_output);
  523|    576|}
_ZNK7simdutf8westmere14implementation34convert_utf8_to_latin1_with_errorsEPKcmPc:
  526|    500|    const char *buf, size_t len, char *latin1_output) const noexcept {
  527|    500|  utf8_to_latin1::validating_transcoder converter;
  528|    500|  return converter.convert_with_errors(buf, len, latin1_output);
  529|    500|}
_ZNK7simdutf8westmere14implementation23convert_utf8_to_utf16leEPKcmPDs:
  539|    754|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  540|    754|  utf8_to_utf16::validating_transcoder converter;
  541|    754|  return converter.convert<endianness::LITTLE>(buf, len, utf16_output);
  542|    754|}
_ZNK7simdutf8westmere14implementation23convert_utf8_to_utf16beEPKcmPDs:
  545|    758|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  546|    758|  utf8_to_utf16::validating_transcoder converter;
  547|    758|  return converter.convert<endianness::BIG>(buf, len, utf16_output);
  548|    758|}
_ZNK7simdutf8westmere14implementation35convert_utf8_to_utf16le_with_errorsEPKcmPDs:
  551|    568|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  552|    568|  utf8_to_utf16::validating_transcoder converter;
  553|    568|  return converter.convert_with_errors<endianness::LITTLE>(buf, len,
  554|    568|                                                           utf16_output);
  555|    568|}
_ZNK7simdutf8westmere14implementation35convert_utf8_to_utf16be_with_errorsEPKcmPDs:
  558|    758|    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
  559|    758|  utf8_to_utf16::validating_transcoder converter;
  560|    758|  return converter.convert_with_errors<endianness::BIG>(buf, len, utf16_output);
  561|    758|}
_ZNK7simdutf8westmere14implementation29convert_valid_utf8_to_utf16leEPKcmPDs:
  564|    438|    const char *input, size_t size, char16_t *utf16_output) const noexcept {
  565|    438|  return utf8_to_utf16::convert_valid<endianness::LITTLE>(input, size,
  566|    438|                                                          utf16_output);
  567|    438|}
_ZNK7simdutf8westmere14implementation29convert_valid_utf8_to_utf16beEPKcmPDs:
  570|    456|    const char *input, size_t size, char16_t *utf16_output) const noexcept {
  571|    456|  return utf8_to_utf16::convert_valid<endianness::BIG>(input, size,
  572|    456|                                                       utf16_output);
  573|    456|}
_ZNK7simdutf8westmere14implementation21convert_utf8_to_utf32EPKcmPDi:
  578|    758|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  579|    758|  utf8_to_utf32::validating_transcoder converter;
  580|    758|  return converter.convert(buf, len, utf32_output);
  581|    758|}
_ZNK7simdutf8westmere14implementation33convert_utf8_to_utf32_with_errorsEPKcmPDi:
  584|    764|    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
  585|    764|  utf8_to_utf32::validating_transcoder converter;
  586|    764|  return converter.convert_with_errors(buf, len, utf32_output);
  587|    764|}
_ZNK7simdutf8westmere14implementation27convert_valid_utf8_to_utf32EPKcmPDi:
  590|    454|    const char *input, size_t size, char32_t *utf32_output) const noexcept {
  591|    454|  return utf8_to_utf32::convert_valid(input, size, utf32_output);
  592|    454|}
_ZNK7simdutf8westmere14implementation25convert_utf16le_to_latin1EPKDsmPc:
  597|    132|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  598|    132|  std::pair<const char16_t *, char *> ret =
  599|    132|      sse_convert_utf16_to_latin1<endianness::LITTLE>(buf, len, latin1_output);
  600|    132|  if (ret.first == nullptr) {
  ------------------
  |  Branch (600:7): [True: 62, False: 70]
  ------------------
  601|     62|    return 0;
  602|     62|  }
  603|     70|  size_t saved_bytes = ret.second - latin1_output;
  604|       |
  605|     70|  if (ret.first != buf + len) {
  ------------------
  |  Branch (605:7): [True: 52, False: 18]
  ------------------
  606|     52|    const size_t scalar_saved_bytes =
  607|     52|        scalar::utf16_to_latin1::convert<endianness::LITTLE>(
  608|     52|            ret.first, len - (ret.first - buf), ret.second);
  609|     52|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (609:9): [True: 30, False: 22]
  ------------------
  610|     30|      return 0;
  611|     30|    }
  612|     22|    saved_bytes += scalar_saved_bytes;
  613|     22|  }
  614|     40|  return saved_bytes;
  615|     70|}
_ZNK7simdutf8westmere14implementation25convert_utf16be_to_latin1EPKDsmPc:
  618|    102|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  619|    102|  std::pair<const char16_t *, char *> ret =
  620|    102|      sse_convert_utf16_to_latin1<endianness::BIG>(buf, len, latin1_output);
  621|    102|  if (ret.first == nullptr) {
  ------------------
  |  Branch (621:7): [True: 36, False: 66]
  ------------------
  622|     36|    return 0;
  623|     36|  }
  624|     66|  size_t saved_bytes = ret.second - latin1_output;
  625|       |
  626|     66|  if (ret.first != buf + len) {
  ------------------
  |  Branch (626:7): [True: 48, False: 18]
  ------------------
  627|     48|    const size_t scalar_saved_bytes =
  628|     48|        scalar::utf16_to_latin1::convert<endianness::BIG>(
  629|     48|            ret.first, len - (ret.first - buf), ret.second);
  630|     48|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (630:9): [True: 24, False: 24]
  ------------------
  631|     24|      return 0;
  632|     24|    }
  633|     24|    saved_bytes += scalar_saved_bytes;
  634|     24|  }
  635|     42|  return saved_bytes;
  636|     66|}
_ZNK7simdutf8westmere14implementation37convert_utf16le_to_latin1_with_errorsEPKDsmPc:
  640|    282|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  641|    282|  std::pair<result, char *> ret =
  642|    282|      sse_convert_utf16_to_latin1_with_errors<endianness::LITTLE>(
  643|    282|          buf, len, latin1_output);
  644|    282|  if (ret.first.error) {
  ------------------
  |  Branch (644:7): [True: 150, False: 132]
  ------------------
  645|    150|    return ret.first;
  646|    150|  } // Can return directly since scalar fallback already found correct
  647|       |    // ret.first.count
  648|    132|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (648:7): [True: 76, False: 56]
  ------------------
  649|     76|    result scalar_res =
  650|     76|        scalar::utf16_to_latin1::convert_with_errors<endianness::LITTLE>(
  651|     76|            buf + ret.first.count, len - ret.first.count, ret.second);
  652|     76|    if (scalar_res.error) {
  ------------------
  |  Branch (652:9): [True: 26, False: 50]
  ------------------
  653|     26|      scalar_res.count += ret.first.count;
  654|     26|      return scalar_res;
  655|     50|    } else {
  656|     50|      ret.second += scalar_res.count;
  657|     50|    }
  658|     76|  }
  659|    106|  ret.first.count =
  660|    106|      ret.second -
  661|    106|      latin1_output; // Set count to the number of 8-bit code units written
  662|    106|  return ret.first;
  663|    132|}
_ZNK7simdutf8westmere14implementation37convert_utf16be_to_latin1_with_errorsEPKDsmPc:
  667|    248|    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
  668|    248|  std::pair<result, char *> ret =
  669|    248|      sse_convert_utf16_to_latin1_with_errors<endianness::BIG>(buf, len,
  670|    248|                                                               latin1_output);
  671|    248|  if (ret.first.error) {
  ------------------
  |  Branch (671:7): [True: 130, False: 118]
  ------------------
  672|    130|    return ret.first;
  673|    130|  } // Can return directly since scalar fallback already found correct
  674|       |    // ret.first.count
  675|    118|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (675:7): [True: 74, False: 44]
  ------------------
  676|     74|    result scalar_res =
  677|     74|        scalar::utf16_to_latin1::convert_with_errors<endianness::BIG>(
  678|     74|            buf + ret.first.count, len - ret.first.count, ret.second);
  679|     74|    if (scalar_res.error) {
  ------------------
  |  Branch (679:9): [True: 30, False: 44]
  ------------------
  680|     30|      scalar_res.count += ret.first.count;
  681|     30|      return scalar_res;
  682|     44|    } else {
  683|     44|      ret.second += scalar_res.count;
  684|     44|    }
  685|     74|  }
  686|     88|  ret.first.count =
  687|     88|      ret.second -
  688|     88|      latin1_output; // Set count to the number of 8-bit code units written
  689|     88|  return ret.first;
  690|    118|}
_ZNK7simdutf8westmere14implementation23convert_utf16le_to_utf8EPKDsmPc:
  707|    838|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  708|    838|  std::pair<const char16_t *, char *> ret =
  709|    838|      sse_convert_utf16_to_utf8<endianness::LITTLE>(buf, len, utf8_output);
  710|    838|  if (ret.first == nullptr) {
  ------------------
  |  Branch (710:7): [True: 110, False: 728]
  ------------------
  711|    110|    return 0;
  712|    110|  }
  713|    728|  size_t saved_bytes = ret.second - utf8_output;
  714|    728|  if (ret.first != buf + len) {
  ------------------
  |  Branch (714:7): [True: 726, False: 2]
  ------------------
  715|    726|    const size_t scalar_saved_bytes =
  716|    726|        scalar::utf16_to_utf8::convert<endianness::LITTLE>(
  717|    726|            ret.first, len - (ret.first - buf), ret.second);
  718|    726|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (718:9): [True: 106, False: 620]
  ------------------
  719|    106|      return 0;
  720|    106|    }
  721|    620|    saved_bytes += scalar_saved_bytes;
  722|    620|  }
  723|    622|  return saved_bytes;
  724|    728|}
_ZNK7simdutf8westmere14implementation23convert_utf16be_to_utf8EPKDsmPc:
  727|    742|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  728|    742|  std::pair<const char16_t *, char *> ret =
  729|    742|      sse_convert_utf16_to_utf8<endianness::BIG>(buf, len, utf8_output);
  730|    742|  if (ret.first == nullptr) {
  ------------------
  |  Branch (730:7): [True: 90, False: 652]
  ------------------
  731|     90|    return 0;
  732|     90|  }
  733|    652|  size_t saved_bytes = ret.second - utf8_output;
  734|    652|  if (ret.first != buf + len) {
  ------------------
  |  Branch (734:7): [True: 650, False: 2]
  ------------------
  735|    650|    const size_t scalar_saved_bytes =
  736|    650|        scalar::utf16_to_utf8::convert<endianness::BIG>(
  737|    650|            ret.first, len - (ret.first - buf), ret.second);
  738|    650|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (738:9): [True: 70, False: 580]
  ------------------
  739|     70|      return 0;
  740|     70|    }
  741|    580|    saved_bytes += scalar_saved_bytes;
  742|    580|  }
  743|    582|  return saved_bytes;
  744|    652|}
_ZNK7simdutf8westmere14implementation35convert_utf16le_to_utf8_with_errorsEPKDsmPc:
  747|    678|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  748|       |  // ret.first.count is always the position in the buffer, not the number of
  749|       |  // code units written even if finished
  750|    678|  std::pair<result, char *> ret =
  751|    678|      westmere::sse_convert_utf16_to_utf8_with_errors<endianness::LITTLE>(
  752|    678|          buf, len, utf8_output);
  753|    678|  if (ret.first.error) {
  ------------------
  |  Branch (753:7): [True: 160, False: 518]
  ------------------
  754|    160|    return ret.first;
  755|    160|  } // Can return directly since scalar fallback already found correct
  756|       |    // ret.first.count
  757|    518|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (757:7): [True: 514, False: 4]
  ------------------
  758|    514|    result scalar_res =
  759|    514|        scalar::utf16_to_utf8::convert_with_errors<endianness::LITTLE>(
  760|    514|            buf + ret.first.count, len - ret.first.count, ret.second);
  761|    514|    if (scalar_res.error) {
  ------------------
  |  Branch (761:9): [True: 122, False: 392]
  ------------------
  762|    122|      scalar_res.count += ret.first.count;
  763|    122|      return scalar_res;
  764|    392|    } else {
  765|    392|      ret.second += scalar_res.count;
  766|    392|    }
  767|    514|  }
  768|    396|  ret.first.count =
  769|    396|      ret.second -
  770|    396|      utf8_output; // Set count to the number of 8-bit code units written
  771|    396|  return ret.first;
  772|    518|}
_ZNK7simdutf8westmere14implementation35convert_utf16be_to_utf8_with_errorsEPKDsmPc:
  775|    716|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  776|       |  // ret.first.count is always the position in the buffer, not the number of
  777|       |  // code units written even if finished
  778|    716|  std::pair<result, char *> ret =
  779|    716|      westmere::sse_convert_utf16_to_utf8_with_errors<endianness::BIG>(
  780|    716|          buf, len, utf8_output);
  781|    716|  if (ret.first.error) {
  ------------------
  |  Branch (781:7): [True: 178, False: 538]
  ------------------
  782|    178|    return ret.first;
  783|    178|  } // Can return directly since scalar fallback already found correct
  784|       |    // ret.first.count
  785|    538|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (785:7): [True: 534, False: 4]
  ------------------
  786|    534|    result scalar_res =
  787|    534|        scalar::utf16_to_utf8::convert_with_errors<endianness::BIG>(
  788|    534|            buf + ret.first.count, len - ret.first.count, ret.second);
  789|    534|    if (scalar_res.error) {
  ------------------
  |  Branch (789:9): [True: 116, False: 418]
  ------------------
  790|    116|      scalar_res.count += ret.first.count;
  791|    116|      return scalar_res;
  792|    418|    } else {
  793|    418|      ret.second += scalar_res.count;
  794|    418|    }
  795|    534|  }
  796|    422|  ret.first.count =
  797|    422|      ret.second -
  798|    422|      utf8_output; // Set count to the number of 8-bit code units written
  799|    422|  return ret.first;
  800|    538|}
_ZNK7simdutf8westmere14implementation29convert_valid_utf16le_to_utf8EPKDsmPc:
  803|    428|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  804|    428|  return convert_utf16le_to_utf8(buf, len, utf8_output);
  805|    428|}
_ZNK7simdutf8westmere14implementation29convert_valid_utf16be_to_utf8EPKDsmPc:
  808|    400|    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
  809|    400|  return convert_utf16be_to_utf8(buf, len, utf8_output);
  810|    400|}
_ZNK7simdutf8westmere14implementation23convert_utf32_to_latin1EPKDimPc:
  815|    178|    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
  816|    178|  std::pair<const char32_t *, char *> ret =
  817|    178|      sse_convert_utf32_to_latin1(buf, len, latin1_output);
  818|    178|  if (ret.first == nullptr) {
  ------------------
  |  Branch (818:7): [True: 28, False: 150]
  ------------------
  819|     28|    return 0;
  820|     28|  }
  821|    150|  size_t saved_bytes = ret.second - latin1_output;
  822|       |  // if (ret.first != buf + len) {
  823|    150|  if (ret.first < buf + len) {
  ------------------
  |  Branch (823:7): [True: 126, False: 24]
  ------------------
  824|    126|    const size_t scalar_saved_bytes = scalar::utf32_to_latin1::convert(
  825|    126|        ret.first, len - (ret.first - buf), ret.second);
  826|    126|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (826:9): [True: 100, False: 26]
  ------------------
  827|    100|      return 0;
  828|    100|    }
  829|     26|    saved_bytes += scalar_saved_bytes;
  830|     26|  }
  831|     50|  return saved_bytes;
  832|    150|}
_ZNK7simdutf8westmere14implementation35convert_utf32_to_latin1_with_errorsEPKDimPc:
  835|    408|    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
  836|       |  // ret.first.count is always the position in the buffer, not the number of
  837|       |  // code units written even if finished
  838|    408|  std::pair<result, char *> ret =
  839|    408|      westmere::sse_convert_utf32_to_latin1_with_errors(buf, len,
  840|    408|                                                        latin1_output);
  841|    408|  if (ret.first.count != len) {
  ------------------
  |  Branch (841:7): [True: 382, False: 26]
  ------------------
  842|    382|    result scalar_res = scalar::utf32_to_latin1::convert_with_errors(
  843|    382|        buf + ret.first.count, len - ret.first.count, ret.second);
  844|    382|    if (scalar_res.error) {
  ------------------
  |  Branch (844:9): [True: 364, False: 18]
  ------------------
  845|    364|      scalar_res.count += ret.first.count;
  846|    364|      return scalar_res;
  847|    364|    } else {
  848|     18|      ret.second += scalar_res.count;
  849|     18|    }
  850|    382|  }
  851|     44|  ret.first.count =
  852|     44|      ret.second -
  853|     44|      latin1_output; // Set count to the number of 8-bit code units written
  854|     44|  return ret.first;
  855|    408|}
_ZNK7simdutf8westmere14implementation21convert_utf32_to_utf8EPKDimPc:
  866|    860|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  867|    860|  std::pair<const char32_t *, char *> ret =
  868|    860|      sse_convert_utf32_to_utf8(buf, len, utf8_output);
  869|    860|  if (ret.first == nullptr) {
  ------------------
  |  Branch (869:7): [True: 250, False: 610]
  ------------------
  870|    250|    return 0;
  871|    250|  }
  872|    610|  size_t saved_bytes = ret.second - utf8_output;
  873|    610|  if (ret.first != buf + len) {
  ------------------
  |  Branch (873:7): [True: 608, False: 2]
  ------------------
  874|    608|    const size_t scalar_saved_bytes = scalar::utf32_to_utf8::convert(
  875|    608|        ret.first, len - (ret.first - buf), ret.second);
  876|    608|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (876:9): [True: 286, False: 322]
  ------------------
  877|    286|      return 0;
  878|    286|    }
  879|    322|    saved_bytes += scalar_saved_bytes;
  880|    322|  }
  881|    324|  return saved_bytes;
  882|    610|}
_ZNK7simdutf8westmere14implementation33convert_utf32_to_utf8_with_errorsEPKDimPc:
  885|    892|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
  886|       |  // ret.first.count is always the position in the buffer, not the number of
  887|       |  // code units written even if finished
  888|    892|  std::pair<result, char *> ret =
  889|    892|      westmere::sse_convert_utf32_to_utf8_with_errors(buf, len, utf8_output);
  890|    892|  if (ret.first.count != len) {
  ------------------
  |  Branch (890:7): [True: 888, False: 4]
  ------------------
  891|    888|    result scalar_res = scalar::utf32_to_utf8::convert_with_errors(
  892|    888|        buf + ret.first.count, len - ret.first.count, ret.second);
  893|    888|    if (scalar_res.error) {
  ------------------
  |  Branch (893:9): [True: 670, False: 218]
  ------------------
  894|    670|      scalar_res.count += ret.first.count;
  895|    670|      return scalar_res;
  896|    670|    } else {
  897|    218|      ret.second += scalar_res.count;
  898|    218|    }
  899|    888|  }
  900|    222|  ret.first.count =
  901|    222|      ret.second -
  902|    222|      utf8_output; // Set count to the number of 8-bit code units written
  903|    222|  return ret.first;
  904|    892|}
_ZNK7simdutf8westmere14implementation24convert_utf16le_to_utf32EPKDsmPDi:
  909|    414|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  910|    414|  std::pair<const char16_t *, char32_t *> ret =
  911|    414|      sse_convert_utf16_to_utf32<endianness::LITTLE>(buf, len, utf32_output);
  912|    414|  if (ret.first == nullptr) {
  ------------------
  |  Branch (912:7): [True: 114, False: 300]
  ------------------
  913|    114|    return 0;
  914|    114|  }
  915|    300|  size_t saved_bytes = ret.second - utf32_output;
  916|    300|  if (ret.first != buf + len) {
  ------------------
  |  Branch (916:7): [True: 234, False: 66]
  ------------------
  917|    234|    const size_t scalar_saved_bytes =
  918|    234|        scalar::utf16_to_utf32::convert<endianness::LITTLE>(
  919|    234|            ret.first, len - (ret.first - buf), ret.second);
  920|    234|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (920:9): [True: 68, False: 166]
  ------------------
  921|     68|      return 0;
  922|     68|    }
  923|    166|    saved_bytes += scalar_saved_bytes;
  924|    166|  }
  925|    232|  return saved_bytes;
  926|    300|}
_ZNK7simdutf8westmere14implementation24convert_utf16be_to_utf32EPKDsmPDi:
  929|    384|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  930|    384|  std::pair<const char16_t *, char32_t *> ret =
  931|    384|      sse_convert_utf16_to_utf32<endianness::BIG>(buf, len, utf32_output);
  932|    384|  if (ret.first == nullptr) {
  ------------------
  |  Branch (932:7): [True: 100, False: 284]
  ------------------
  933|    100|    return 0;
  934|    100|  }
  935|    284|  size_t saved_bytes = ret.second - utf32_output;
  936|    284|  if (ret.first != buf + len) {
  ------------------
  |  Branch (936:7): [True: 218, False: 66]
  ------------------
  937|    218|    const size_t scalar_saved_bytes =
  938|    218|        scalar::utf16_to_utf32::convert<endianness::BIG>(
  939|    218|            ret.first, len - (ret.first - buf), ret.second);
  940|    218|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (940:9): [True: 56, False: 162]
  ------------------
  941|     56|      return 0;
  942|     56|    }
  943|    162|    saved_bytes += scalar_saved_bytes;
  944|    162|  }
  945|    228|  return saved_bytes;
  946|    284|}
_ZNK7simdutf8westmere14implementation36convert_utf16le_to_utf32_with_errorsEPKDsmPDi:
  949|    408|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  950|       |  // ret.first.count is always the position in the buffer, not the number of
  951|       |  // code units written even if finished
  952|    408|  std::pair<result, char32_t *> ret =
  953|    408|      westmere::sse_convert_utf16_to_utf32_with_errors<endianness::LITTLE>(
  954|    408|          buf, len, utf32_output);
  955|    408|  if (ret.first.error) {
  ------------------
  |  Branch (955:7): [True: 160, False: 248]
  ------------------
  956|    160|    return ret.first;
  957|    160|  } // Can return directly since scalar fallback already found correct
  958|       |    // ret.first.count
  959|    248|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (959:7): [True: 206, False: 42]
  ------------------
  960|    206|    result scalar_res =
  961|    206|        scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
  962|    206|            buf + ret.first.count, len - ret.first.count, ret.second);
  963|    206|    if (scalar_res.error) {
  ------------------
  |  Branch (963:9): [True: 70, False: 136]
  ------------------
  964|     70|      scalar_res.count += ret.first.count;
  965|     70|      return scalar_res;
  966|    136|    } else {
  967|    136|      ret.second += scalar_res.count;
  968|    136|    }
  969|    206|  }
  970|    178|  ret.first.count =
  971|    178|      ret.second -
  972|    178|      utf32_output; // Set count to the number of 8-bit code units written
  973|    178|  return ret.first;
  974|    248|}
_ZNK7simdutf8westmere14implementation36convert_utf16be_to_utf32_with_errorsEPKDsmPDi:
  977|    362|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
  978|       |  // ret.first.count is always the position in the buffer, not the number of
  979|       |  // code units written even if finished
  980|    362|  std::pair<result, char32_t *> ret =
  981|    362|      westmere::sse_convert_utf16_to_utf32_with_errors<endianness::BIG>(
  982|    362|          buf, len, utf32_output);
  983|    362|  if (ret.first.error) {
  ------------------
  |  Branch (983:7): [True: 100, False: 262]
  ------------------
  984|    100|    return ret.first;
  985|    100|  } // Can return directly since scalar fallback already found correct
  986|       |    // ret.first.count
  987|    262|  if (ret.first.count != len) { // All good so far, but not finished
  ------------------
  |  Branch (987:7): [True: 218, False: 44]
  ------------------
  988|    218|    result scalar_res =
  989|    218|        scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
  990|    218|            buf + ret.first.count, len - ret.first.count, ret.second);
  991|    218|    if (scalar_res.error) {
  ------------------
  |  Branch (991:9): [True: 88, False: 130]
  ------------------
  992|     88|      scalar_res.count += ret.first.count;
  993|     88|      return scalar_res;
  994|    130|    } else {
  995|    130|      ret.second += scalar_res.count;
  996|    130|    }
  997|    218|  }
  998|    174|  ret.first.count =
  999|    174|      ret.second -
 1000|    174|      utf32_output; // Set count to the number of 8-bit code units written
 1001|    174|  return ret.first;
 1002|    262|}
_ZNK7simdutf8westmere14implementation27convert_valid_utf32_to_utf8EPKDimPc:
 1007|    224|    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
 1008|    224|  return convert_utf32_to_utf8(buf, len, utf8_output);
 1009|    224|}
_ZNK7simdutf8westmere14implementation24convert_utf32_to_utf16leEPKDimPDs:
 1014|    738|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1015|    738|  std::pair<const char32_t *, char16_t *> ret =
 1016|    738|      sse_convert_utf32_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
 1017|    738|  if (ret.first == nullptr) {
  ------------------
  |  Branch (1017:7): [True: 46, False: 692]
  ------------------
 1018|     46|    return 0;
 1019|     46|  }
 1020|    692|  size_t saved_bytes = ret.second - utf16_output;
 1021|    692|  if (ret.first != buf + len) {
  ------------------
  |  Branch (1021:7): [True: 688, False: 4]
  ------------------
 1022|    688|    const size_t scalar_saved_bytes =
 1023|    688|        scalar::utf32_to_utf16::convert<endianness::LITTLE>(
 1024|    688|            ret.first, len - (ret.first - buf), ret.second);
 1025|    688|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (1025:9): [True: 306, False: 382]
  ------------------
 1026|    306|      return 0;
 1027|    306|    }
 1028|    382|    saved_bytes += scalar_saved_bytes;
 1029|    382|  }
 1030|    386|  return saved_bytes;
 1031|    692|}
_ZNK7simdutf8westmere14implementation24convert_utf32_to_utf16beEPKDimPDs:
 1034|    634|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1035|    634|  std::pair<const char32_t *, char16_t *> ret =
 1036|    634|      sse_convert_utf32_to_utf16<endianness::BIG>(buf, len, utf16_output);
 1037|    634|  if (ret.first == nullptr) {
  ------------------
  |  Branch (1037:7): [True: 110, False: 524]
  ------------------
 1038|    110|    return 0;
 1039|    110|  }
 1040|    524|  size_t saved_bytes = ret.second - utf16_output;
 1041|    524|  if (ret.first != buf + len) {
  ------------------
  |  Branch (1041:7): [True: 522, False: 2]
  ------------------
 1042|    522|    const size_t scalar_saved_bytes =
 1043|    522|        scalar::utf32_to_utf16::convert<endianness::BIG>(
 1044|    522|            ret.first, len - (ret.first - buf), ret.second);
 1045|    522|    if (scalar_saved_bytes == 0) {
  ------------------
  |  Branch (1045:9): [True: 264, False: 258]
  ------------------
 1046|    264|      return 0;
 1047|    264|    }
 1048|    258|    saved_bytes += scalar_saved_bytes;
 1049|    258|  }
 1050|    260|  return saved_bytes;
 1051|    524|}
_ZNK7simdutf8westmere14implementation36convert_utf32_to_utf16le_with_errorsEPKDimPDs:
 1054|    658|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1055|       |  // ret.first.count is always the position in the buffer, not the number of
 1056|       |  // code units written even if finished
 1057|    658|  std::pair<result, char16_t *> ret =
 1058|    658|      westmere::sse_convert_utf32_to_utf16_with_errors<endianness::LITTLE>(
 1059|    658|          buf, len, utf16_output);
 1060|    658|  if (ret.first.count != len) {
  ------------------
  |  Branch (1060:7): [True: 634, False: 24]
  ------------------
 1061|    634|    result scalar_res =
 1062|    634|        scalar::utf32_to_utf16::convert_with_errors<endianness::LITTLE>(
 1063|    634|            buf + ret.first.count, len - ret.first.count, ret.second);
 1064|    634|    if (scalar_res.error) {
  ------------------
  |  Branch (1064:9): [True: 482, False: 152]
  ------------------
 1065|    482|      scalar_res.count += ret.first.count;
 1066|    482|      return scalar_res;
 1067|    482|    } else {
 1068|    152|      ret.second += scalar_res.count;
 1069|    152|    }
 1070|    634|  }
 1071|    176|  ret.first.count =
 1072|    176|      ret.second -
 1073|    176|      utf16_output; // Set count to the number of 8-bit code units written
 1074|    176|  return ret.first;
 1075|    658|}
_ZNK7simdutf8westmere14implementation36convert_utf32_to_utf16be_with_errorsEPKDimPDs:
 1078|    592|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1079|       |  // ret.first.count is always the position in the buffer, not the number of
 1080|       |  // code units written even if finished
 1081|    592|  std::pair<result, char16_t *> ret =
 1082|    592|      westmere::sse_convert_utf32_to_utf16_with_errors<endianness::BIG>(
 1083|    592|          buf, len, utf16_output);
 1084|    592|  if (ret.first.count != len) {
  ------------------
  |  Branch (1084:7): [True: 570, False: 22]
  ------------------
 1085|    570|    result scalar_res =
 1086|    570|        scalar::utf32_to_utf16::convert_with_errors<endianness::BIG>(
 1087|    570|            buf + ret.first.count, len - ret.first.count, ret.second);
 1088|    570|    if (scalar_res.error) {
  ------------------
  |  Branch (1088:9): [True: 414, False: 156]
  ------------------
 1089|    414|      scalar_res.count += ret.first.count;
 1090|    414|      return scalar_res;
 1091|    414|    } else {
 1092|    156|      ret.second += scalar_res.count;
 1093|    156|    }
 1094|    570|  }
 1095|    178|  ret.first.count =
 1096|    178|      ret.second -
 1097|    178|      utf16_output; // Set count to the number of 8-bit code units written
 1098|    178|  return ret.first;
 1099|    592|}
_ZNK7simdutf8westmere14implementation30convert_valid_utf32_to_utf16leEPKDimPDs:
 1102|    242|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1103|    242|  return convert_utf32_to_utf16le(buf, len, utf16_output);
 1104|    242|}
_ZNK7simdutf8westmere14implementation30convert_valid_utf32_to_utf16beEPKDimPDs:
 1107|    126|    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
 1108|    126|  return convert_utf32_to_utf16be(buf, len, utf16_output);
 1109|    126|}
_ZNK7simdutf8westmere14implementation30convert_valid_utf16le_to_utf32EPKDsmPDi:
 1112|    120|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
 1113|    120|  return convert_utf16le_to_utf32(buf, len, utf32_output);
 1114|    120|}
_ZNK7simdutf8westmere14implementation30convert_valid_utf16be_to_utf32EPKDsmPDi:
 1117|    146|    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
 1118|    146|  return convert_utf16be_to_utf32(buf, len, utf32_output);
 1119|    146|}
_ZNK7simdutf8westmere14implementation13count_utf16leEPKDsm:
 1130|  1.39k|    const char16_t *input, size_t length) const noexcept {
 1131|  1.39k|  return utf16::count_code_points<endianness::LITTLE>(input, length);
 1132|  1.39k|}
_ZNK7simdutf8westmere14implementation13count_utf16beEPKDsm:
 1135|  1.30k|    const char16_t *input, size_t length) const noexcept {
 1136|  1.30k|  return utf16::count_code_points<endianness::BIG>(input, length);
 1137|  1.30k|}
_ZNK7simdutf8westmere14implementation10count_utf8EPKcm:
 1142|  3.96k|implementation::count_utf8(const char *input, size_t length) const noexcept {
 1143|  3.96k|  return utf8::count_code_points_bytemask(input, length);
 1144|  3.96k|}
_ZNK7simdutf8westmere14implementation23latin1_length_from_utf8EPKcm:
 1149|    538|    const char *buf, size_t len) const noexcept {
 1150|    538|  return count_utf8(buf, len);
 1151|    538|}
_ZNK7simdutf8westmere14implementation24utf8_length_from_utf16leEPKDsm:
 1156|    767|    const char16_t *input, size_t length) const noexcept {
 1157|    767|  return utf16::utf8_length_from_utf16_bytemask<endianness::LITTLE>(input,
 1158|    767|                                                                    length);
 1159|    767|}
_ZNK7simdutf8westmere14implementation24utf8_length_from_utf16beEPKDsm:
 1162|    741|    const char16_t *input, size_t length) const noexcept {
 1163|    741|  return utf16::utf8_length_from_utf16_bytemask<endianness::BIG>(input, length);
 1164|    741|}
_ZNK7simdutf8westmere14implementation23utf8_length_from_latin1EPKcm:
 1169|    307|    const char *input, size_t len) const noexcept {
 1170|    307|  const uint8_t *str = reinterpret_cast<const uint8_t *>(input);
 1171|    307|  size_t answer = len / sizeof(__m128i) * sizeof(__m128i);
 1172|    307|  size_t i = 0;
 1173|    307|  if (answer >= 2048) { // long strings optimization
  ------------------
  |  Branch (1173:7): [True: 157, False: 150]
  ------------------
 1174|    157|    __m128i two_64bits = _mm_setzero_si128();
 1175|  6.10k|    while (i + sizeof(__m128i) <= len) {
  ------------------
  |  Branch (1175:12): [True: 5.95k, False: 157]
  ------------------
 1176|  5.95k|      __m128i runner = _mm_setzero_si128();
 1177|  5.95k|      size_t iterations = (len - i) / sizeof(__m128i);
 1178|  5.95k|      if (iterations > 255) {
  ------------------
  |  Branch (1178:11): [True: 5.79k, False: 157]
  ------------------
 1179|  5.79k|        iterations = 255;
 1180|  5.79k|      }
 1181|  5.95k|      size_t max_i = i + iterations * sizeof(__m128i) - sizeof(__m128i);
 1182|   374k|      for (; i + 4 * sizeof(__m128i) <= max_i; i += 4 * sizeof(__m128i)) {
  ------------------
  |  Branch (1182:14): [True: 368k, False: 5.95k]
  ------------------
 1183|   368k|        __m128i input1 = _mm_loadu_si128((const __m128i *)(str + i));
 1184|   368k|        __m128i input2 =
 1185|   368k|            _mm_loadu_si128((const __m128i *)(str + i + sizeof(__m128i)));
 1186|   368k|        __m128i input3 =
 1187|   368k|            _mm_loadu_si128((const __m128i *)(str + i + 2 * sizeof(__m128i)));
 1188|   368k|        __m128i input4 =
 1189|   368k|            _mm_loadu_si128((const __m128i *)(str + i + 3 * sizeof(__m128i)));
 1190|   368k|        __m128i input12 =
 1191|   368k|            _mm_add_epi8(_mm_cmpgt_epi8(_mm_setzero_si128(), input1),
 1192|   368k|                         _mm_cmpgt_epi8(_mm_setzero_si128(), input2));
 1193|   368k|        __m128i input34 =
 1194|   368k|            _mm_add_epi8(_mm_cmpgt_epi8(_mm_setzero_si128(), input3),
 1195|   368k|                         _mm_cmpgt_epi8(_mm_setzero_si128(), input4));
 1196|   368k|        __m128i input1234 = _mm_add_epi8(input12, input34);
 1197|   368k|        runner = _mm_sub_epi8(runner, input1234);
 1198|   368k|      }
 1199|  23.7k|      for (; i <= max_i; i += sizeof(__m128i)) {
  ------------------
  |  Branch (1199:14): [True: 17.7k, False: 5.95k]
  ------------------
 1200|  17.7k|        __m128i more_input = _mm_loadu_si128((const __m128i *)(str + i));
 1201|  17.7k|        runner = _mm_sub_epi8(runner,
 1202|  17.7k|                              _mm_cmpgt_epi8(_mm_setzero_si128(), more_input));
 1203|  17.7k|      }
 1204|  5.95k|      two_64bits =
 1205|  5.95k|          _mm_add_epi64(two_64bits, _mm_sad_epu8(runner, _mm_setzero_si128()));
 1206|  5.95k|    }
 1207|    157|    answer +=
 1208|    157|        _mm_extract_epi64(two_64bits, 0) + _mm_extract_epi64(two_64bits, 1);
 1209|    157|  } else if (answer > 0) { // short string optimization
  ------------------
  |  Branch (1209:14): [True: 100, False: 50]
  ------------------
 1210|  1.17k|    for (; i + 2 * sizeof(__m128i) <= len; i += 2 * sizeof(__m128i)) {
  ------------------
  |  Branch (1210:12): [True: 1.07k, False: 100]
  ------------------
 1211|  1.07k|      __m128i latin = _mm_loadu_si128((const __m128i *)(input + i));
 1212|  1.07k|      uint16_t non_ascii = (uint16_t)_mm_movemask_epi8(latin);
 1213|  1.07k|      answer += count_ones(non_ascii);
 1214|  1.07k|      latin = _mm_loadu_si128((const __m128i *)(input + i) + 1);
 1215|  1.07k|      non_ascii = (uint16_t)_mm_movemask_epi8(latin);
 1216|  1.07k|      answer += count_ones(non_ascii);
 1217|  1.07k|    }
 1218|    145|    for (; i + sizeof(__m128i) <= len; i += sizeof(__m128i)) {
  ------------------
  |  Branch (1218:12): [True: 45, False: 100]
  ------------------
 1219|     45|      __m128i latin = _mm_loadu_si128((const __m128i *)(input + i));
 1220|     45|      uint16_t non_ascii = (uint16_t)_mm_movemask_epi8(latin);
 1221|     45|      answer += count_ones(non_ascii);
 1222|     45|    }
 1223|    100|  }
 1224|    307|  return answer + scalar::latin1::utf8_length_from_latin1(
 1225|    307|                      reinterpret_cast<const char *>(str + i), len - i);
 1226|    307|}
_ZNK7simdutf8westmere14implementation25utf32_length_from_utf16leEPKDsm:
 1231|    417|    const char16_t *input, size_t length) const noexcept {
 1232|    417|  return utf16::utf32_length_from_utf16<endianness::LITTLE>(input, length);
 1233|    417|}
_ZNK7simdutf8westmere14implementation25utf32_length_from_utf16beEPKDsm:
 1236|    389|    const char16_t *input, size_t length) const noexcept {
 1237|    389|  return utf16::utf32_length_from_utf16<endianness::BIG>(input, length);
 1238|    389|}
_ZNK7simdutf8westmere14implementation22utf16_length_from_utf8EPKcm:
 1243|  1.88k|    const char *input, size_t length) const noexcept {
 1244|  1.88k|  return utf8::utf16_length_from_utf8_bytemask(input, length);
 1245|  1.88k|}
_ZNK7simdutf8westmere14implementation22utf8_length_from_utf32EPKDim:
 1278|    884|    const char32_t *input, size_t length) const noexcept {
 1279|    884|  return utf32::utf8_length_from_utf32(input, length);
 1280|    884|}
_ZNK7simdutf8westmere14implementation23utf16_length_from_utf32EPKDim:
 1285|  1.32k|    const char32_t *input, size_t length) const noexcept {
 1286|  1.32k|  const __m128i v_00000000 = _mm_setzero_si128();
 1287|  1.32k|  const __m128i v_ffff0000 = _mm_set1_epi32((uint32_t)0xffff0000);
 1288|  1.32k|  size_t pos = 0;
 1289|  1.32k|  size_t count = 0;
 1290|  1.55M|  for (; pos + 4 <= length; pos += 4) {
  ------------------
  |  Branch (1290:10): [True: 1.55M, False: 1.32k]
  ------------------
 1291|  1.55M|    __m128i in = _mm_loadu_si128((__m128i *)(input + pos));
 1292|  1.55M|    const __m128i surrogate_bytemask =
 1293|  1.55M|        _mm_cmpeq_epi32(_mm_and_si128(in, v_ffff0000), v_00000000);
 1294|  1.55M|    const uint16_t surrogate_bitmask =
 1295|  1.55M|        static_cast<uint16_t>(_mm_movemask_epi8(surrogate_bytemask));
 1296|  1.55M|    size_t surrogate_count = (16 - count_ones(surrogate_bitmask)) / 4;
 1297|  1.55M|    count += 4 + surrogate_count;
 1298|  1.55M|  }
 1299|  1.32k|  return count +
 1300|  1.32k|         scalar::utf32::utf16_length_from_utf32(input + pos, length - pos);
 1301|  1.32k|}
_ZNK7simdutf8westmere14implementation22utf32_length_from_utf8EPKcm:
 1306|    995|    const char *input, size_t length) const noexcept {
 1307|    995|  return utf8::count_code_points(input, length);
 1308|    995|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_18is_asciiERKNS1_4simd8simd8x64IhEE:
   13|  4.98M|simdutf_really_inline bool is_ascii(const simd8x64<uint8_t> &input) {
   14|  4.98M|  return input.reduce_or().is_ascii();
   15|  4.98M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_124must_be_2_3_continuationENS1_4simd5simd8IhEES4_:
   22|  7.08M|                         const simd8<uint8_t> prev3) {
   23|  7.08M|  simd8<uint8_t> is_third_byte =
   24|  7.08M|      prev2.saturating_sub(0xe0u - 0x80); // Only 111_____ will be >= 0x80
   25|  7.08M|  simd8<uint8_t> is_fourth_byte =
   26|  7.08M|      prev3.saturating_sub(0xf0u - 0x80); // Only 1111____ will be >= 0x80
   27|  7.08M|  return simd8<bool>(is_third_byte | is_fourth_byte);
   28|  7.08M|}

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

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_127sse_convert_latin1_to_utf16ILNS_10endiannessE0EEENSt3__14pairIPKcPDsEES7_mS8_:
    4|     88|                            char16_t *utf16_output) {
    5|     88|  size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16
    6|   174k|  for (size_t i = 0; i < rounded_len; i += 16) {
  ------------------
  |  Branch (6:22): [True: 174k, False: 88]
  ------------------
    7|       |    // Load 16 Latin1 characters into a 128-bit register
    8|   174k|    __m128i in =
    9|   174k|        _mm_loadu_si128(reinterpret_cast<const __m128i *>(&latin1_input[i]));
   10|   174k|    __m128i out1 = big_endian ? _mm_unpacklo_epi8(_mm_setzero_si128(), in)
  ------------------
  |  Branch (10:20): [Folded, False: 174k]
  ------------------
   11|   174k|                              : _mm_unpacklo_epi8(in, _mm_setzero_si128());
   12|   174k|    __m128i out2 = big_endian ? _mm_unpackhi_epi8(_mm_setzero_si128(), in)
  ------------------
  |  Branch (12:20): [Folded, False: 174k]
  ------------------
   13|   174k|                              : _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|   174k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(&utf16_output[i]), out1);
   17|   174k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(&utf16_output[i + 8]), out2);
   18|   174k|  }
   19|       |  // return pointers pointing to where we left off
   20|     88|  return std::make_pair(latin1_input + rounded_len, utf16_output + rounded_len);
   21|     88|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_127sse_convert_latin1_to_utf16ILNS_10endiannessE1EEENSt3__14pairIPKcPDsEES7_mS8_:
    4|    100|                            char16_t *utf16_output) {
    5|    100|  size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16
    6|   131k|  for (size_t i = 0; i < rounded_len; i += 16) {
  ------------------
  |  Branch (6:22): [True: 131k, False: 100]
  ------------------
    7|       |    // Load 16 Latin1 characters into a 128-bit register
    8|   131k|    __m128i in =
    9|   131k|        _mm_loadu_si128(reinterpret_cast<const __m128i *>(&latin1_input[i]));
   10|   131k|    __m128i out1 = big_endian ? _mm_unpacklo_epi8(_mm_setzero_si128(), in)
  ------------------
  |  Branch (10:20): [True: 131k, Folded]
  ------------------
   11|   131k|                              : _mm_unpacklo_epi8(in, _mm_setzero_si128());
   12|   131k|    __m128i out2 = big_endian ? _mm_unpackhi_epi8(_mm_setzero_si128(), in)
  ------------------
  |  Branch (12:20): [True: 131k, Folded]
  ------------------
   13|   131k|                              : _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|   131k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(&utf16_output[i]), out1);
   17|   131k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(&utf16_output[i + 8]), out2);
   18|   131k|  }
   19|       |  // return pointers pointing to where we left off
   20|    100|  return std::make_pair(latin1_input + rounded_len, utf16_output + rounded_len);
   21|    100|}

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

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

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

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_126sse_convert_utf16_to_utf32ILNS_10endiannessE0EEENSt3__14pairIPKDsPDiEES7_mS8_:
   57|    414|                           char32_t *utf32_output) {
   58|    414|  const char16_t *end = buf + len;
   59|       |
   60|    414|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
   61|    414|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
   62|       |
   63|   341k|  while (end - buf >= 8) {
  ------------------
  |  Branch (63:10): [True: 340k, False: 306]
  ------------------
   64|   340k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
   65|       |
   66|   340k|    if (big_endian) {
  ------------------
  |  Branch (66:9): [Folded, False: 340k]
  ------------------
   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|   340k|    const __m128i surrogates_bytemask =
   76|   340k|        _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|   340k|    const uint16_t surrogates_bitmask =
   81|   340k|        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|   340k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (84:9): [True: 334k, False: 6.33k]
  ------------------
   85|       |      // case: no surrogate pair, extend 16-bit code units to 32-bit code units
   86|   334k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output),
   87|   334k|                       _mm_cvtepu16_epi32(in));
   88|   334k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 4),
   89|   334k|                       _mm_cvtepu16_epi32(_mm_srli_si128(in, 8)));
   90|   334k|      utf32_output += 8;
   91|   334k|      buf += 8;
   92|       |      // surrogate pair(s) in a register
   93|   334k|    } 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.33k|      size_t forward = 15;
   98|  6.33k|      size_t k = 0;
   99|  6.33k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (99:11): [True: 70, False: 6.26k]
  ------------------
  100|     70|        forward = size_t(end - buf - 1);
  101|     70|      }
  102|  92.5k|      for (; k < forward; k++) {
  ------------------
  |  Branch (102:14): [True: 86.3k, False: 6.22k]
  ------------------
  103|  86.3k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  104|  86.3k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (104:13): [True: 78.8k, False: 7.45k]
  ------------------
  105|  78.8k|          *utf32_output++ = char32_t(word);
  106|  78.8k|        } else {
  107|       |          // must be a surrogate pair
  108|  7.45k|          uint16_t diff = uint16_t(word - 0xD800);
  109|  7.45k|          uint16_t next_word =
  110|  7.45k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  111|  7.45k|          k++;
  112|  7.45k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  113|  7.45k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (113:15): [True: 108, False: 7.35k]
  ------------------
  114|    108|            return std::make_pair(nullptr, utf32_output);
  115|    108|          }
  116|  7.35k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  117|  7.35k|          *utf32_output++ = char32_t(value);
  118|  7.35k|        }
  119|  86.3k|      }
  120|  6.22k|      buf += k;
  121|  6.22k|    }
  122|   340k|  } // while
  123|    306|  return std::make_pair(buf, utf32_output);
  124|    414|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_126sse_convert_utf16_to_utf32ILNS_10endiannessE1EEENSt3__14pairIPKDsPDiEES7_mS8_:
   57|    384|                           char32_t *utf32_output) {
   58|    384|  const char16_t *end = buf + len;
   59|       |
   60|    384|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
   61|    384|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
   62|       |
   63|   611k|  while (end - buf >= 8) {
  ------------------
  |  Branch (63:10): [True: 611k, False: 290]
  ------------------
   64|   611k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
   65|       |
   66|   611k|    if (big_endian) {
  ------------------
  |  Branch (66:9): [True: 611k, Folded]
  ------------------
   67|   611k|      const __m128i swap =
   68|   611k|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   69|   611k|      in = _mm_shuffle_epi8(in, swap);
   70|   611k|    }
   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|   611k|    const __m128i surrogates_bytemask =
   76|   611k|        _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|   611k|    const uint16_t surrogates_bitmask =
   81|   611k|        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|   611k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (84:9): [True: 610k, False: 1.13k]
  ------------------
   85|       |      // case: no surrogate pair, extend 16-bit code units to 32-bit code units
   86|   610k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output),
   87|   610k|                       _mm_cvtepu16_epi32(in));
   88|   610k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 4),
   89|   610k|                       _mm_cvtepu16_epi32(_mm_srli_si128(in, 8)));
   90|   610k|      utf32_output += 8;
   91|   610k|      buf += 8;
   92|       |      // surrogate pair(s) in a register
   93|   610k|    } 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|  1.13k|      size_t forward = 15;
   98|  1.13k|      size_t k = 0;
   99|  1.13k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (99:11): [True: 76, False: 1.05k]
  ------------------
  100|     76|        forward = size_t(end - buf - 1);
  101|     76|      }
  102|  14.8k|      for (; k < forward; k++) {
  ------------------
  |  Branch (102:14): [True: 13.8k, False: 1.03k]
  ------------------
  103|  13.8k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  104|  13.8k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (104:13): [True: 11.7k, False: 2.11k]
  ------------------
  105|  11.7k|          *utf32_output++ = char32_t(word);
  106|  11.7k|        } else {
  107|       |          // must be a surrogate pair
  108|  2.11k|          uint16_t diff = uint16_t(word - 0xD800);
  109|  2.11k|          uint16_t next_word =
  110|  2.11k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  111|  2.11k|          k++;
  112|  2.11k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  113|  2.11k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (113:15): [True: 94, False: 2.01k]
  ------------------
  114|     94|            return std::make_pair(nullptr, utf32_output);
  115|     94|          }
  116|  2.01k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  117|  2.01k|          *utf32_output++ = char32_t(value);
  118|  2.01k|        }
  119|  13.8k|      }
  120|  1.03k|      buf += k;
  121|  1.03k|    }
  122|   611k|  } // while
  123|    290|  return std::make_pair(buf, utf32_output);
  124|    384|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_138sse_convert_utf16_to_utf32_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPDiEEPKDsmS7_:
  136|    408|                                       char32_t *utf32_output) {
  137|    408|  const char16_t *start = buf;
  138|    408|  const char16_t *end = buf + len;
  139|       |
  140|    408|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
  141|    408|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
  142|       |
  143|   544k|  while (end - buf >= 8) {
  ------------------
  |  Branch (143:10): [True: 544k, False: 248]
  ------------------
  144|   544k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
  145|       |
  146|   544k|    if (big_endian) {
  ------------------
  |  Branch (146:9): [Folded, False: 544k]
  ------------------
  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|   544k|    const __m128i surrogates_bytemask =
  156|   544k|        _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|   544k|    const uint16_t surrogates_bitmask =
  161|   544k|        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|   544k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (164:9): [True: 534k, False: 9.49k]
  ------------------
  165|       |      // case: no surrogate pair, extend 16-bit code units to 32-bit code units
  166|   534k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output),
  167|   534k|                       _mm_cvtepu16_epi32(in));
  168|   534k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 4),
  169|   534k|                       _mm_cvtepu16_epi32(_mm_srli_si128(in, 8)));
  170|   534k|      utf32_output += 8;
  171|   534k|      buf += 8;
  172|       |      // surrogate pair(s) in a register
  173|   534k|    } 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|  9.49k|      size_t forward = 15;
  178|  9.49k|      size_t k = 0;
  179|  9.49k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (179:11): [True: 86, False: 9.41k]
  ------------------
  180|     86|        forward = size_t(end - buf - 1);
  181|     86|      }
  182|   136k|      for (; k < forward; k++) {
  ------------------
  |  Branch (182:14): [True: 126k, False: 9.33k]
  ------------------
  183|   126k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  184|   126k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (184:13): [True: 112k, False: 14.4k]
  ------------------
  185|   112k|          *utf32_output++ = char32_t(word);
  186|   112k|        } else {
  187|       |          // must be a surrogate pair
  188|  14.4k|          uint16_t diff = uint16_t(word - 0xD800);
  189|  14.4k|          uint16_t next_word =
  190|  14.4k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  191|  14.4k|          k++;
  192|  14.4k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  193|  14.4k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (193:15): [True: 160, False: 14.2k]
  ------------------
  194|    160|            return std::make_pair(
  195|    160|                result(error_code::SURROGATE, buf - start + k - 1),
  196|    160|                utf32_output);
  197|    160|          }
  198|  14.2k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  199|  14.2k|          *utf32_output++ = char32_t(value);
  200|  14.2k|        }
  201|   126k|      }
  202|  9.33k|      buf += k;
  203|  9.33k|    }
  204|   544k|  } // while
  205|    248|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf32_output);
  206|    408|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_138sse_convert_utf16_to_utf32_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPDiEEPKDsmS7_:
  136|    362|                                       char32_t *utf32_output) {
  137|    362|  const char16_t *start = buf;
  138|    362|  const char16_t *end = buf + len;
  139|       |
  140|    362|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
  141|    362|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
  142|       |
  143|   410k|  while (end - buf >= 8) {
  ------------------
  |  Branch (143:10): [True: 409k, False: 262]
  ------------------
  144|   409k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
  145|       |
  146|   409k|    if (big_endian) {
  ------------------
  |  Branch (146:9): [True: 409k, Folded]
  ------------------
  147|   409k|      const __m128i swap =
  148|   409k|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  149|   409k|      in = _mm_shuffle_epi8(in, swap);
  150|   409k|    }
  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|   409k|    const __m128i surrogates_bytemask =
  156|   409k|        _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|   409k|    const uint16_t surrogates_bitmask =
  161|   409k|        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|   409k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (164:9): [True: 408k, False: 1.79k]
  ------------------
  165|       |      // case: no surrogate pair, extend 16-bit code units to 32-bit code units
  166|   408k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output),
  167|   408k|                       _mm_cvtepu16_epi32(in));
  168|   408k|      _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 4),
  169|   408k|                       _mm_cvtepu16_epi32(_mm_srli_si128(in, 8)));
  170|   408k|      utf32_output += 8;
  171|   408k|      buf += 8;
  172|       |      // surrogate pair(s) in a register
  173|   408k|    } 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|  1.79k|      size_t forward = 15;
  178|  1.79k|      size_t k = 0;
  179|  1.79k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (179:11): [True: 92, False: 1.70k]
  ------------------
  180|     92|        forward = size_t(end - buf - 1);
  181|     92|      }
  182|  21.5k|      for (; k < forward; k++) {
  ------------------
  |  Branch (182:14): [True: 19.8k, False: 1.69k]
  ------------------
  183|  19.8k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  184|  19.8k|        if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (184:13): [True: 13.4k, False: 6.40k]
  ------------------
  185|  13.4k|          *utf32_output++ = char32_t(word);
  186|  13.4k|        } else {
  187|       |          // must be a surrogate pair
  188|  6.40k|          uint16_t diff = uint16_t(word - 0xD800);
  189|  6.40k|          uint16_t next_word =
  190|  6.40k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  191|  6.40k|          k++;
  192|  6.40k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  193|  6.40k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (193:15): [True: 100, False: 6.30k]
  ------------------
  194|    100|            return std::make_pair(
  195|    100|                result(error_code::SURROGATE, buf - start + k - 1),
  196|    100|                utf32_output);
  197|    100|          }
  198|  6.30k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  199|  6.30k|          *utf32_output++ = char32_t(value);
  200|  6.30k|        }
  201|  19.8k|      }
  202|  1.69k|      buf += k;
  203|  1.69k|    }
  204|   409k|  } // while
  205|    262|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf32_output);
  206|    362|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_125sse_convert_utf16_to_utf8ILNS_10endiannessE0EEENSt3__14pairIPKDsPcEES7_mS8_:
   56|    838|sse_convert_utf16_to_utf8(const char16_t *buf, size_t len, char *utf8_output) {
   57|       |
   58|    838|  const char16_t *end = buf + len;
   59|       |
   60|    838|  const __m128i v_0000 = _mm_setzero_si128();
   61|    838|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
   62|    838|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
   63|    838|  const size_t safety_margin =
   64|    838|      12; // to avoid overruns, see issue
   65|       |          // https://github.com/simdutf/simdutf/issues/92
   66|       |
   67|  1.13M|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (67:10): [True: 1.13M, False: 732]
  ------------------
   68|  1.13M|    __m128i in = _mm_loadu_si128((__m128i *)buf);
   69|  1.13M|    if (big_endian) {
  ------------------
  |  Branch (69:9): [Folded, False: 1.13M]
  ------------------
   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.13M|    const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80);
   76|  1.13M|    if (_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (76:9): [True: 819k, False: 318k]
  ------------------
   77|   819k|      __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
   78|   819k|      if (big_endian) {
  ------------------
  |  Branch (78:11): [Folded, False: 819k]
  ------------------
   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|   819k|      if (!_mm_testz_si128(nextin, v_ff80)) {
  ------------------
  |  Branch (83:11): [True: 56.0k, False: 763k]
  ------------------
   84|       |        // 1. pack the bytes
   85|       |        // obviously suboptimal.
   86|  56.0k|        const __m128i utf8_packed = _mm_packus_epi16(in, in);
   87|       |        // 2. store (16 bytes)
   88|  56.0k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   89|       |        // 3. adjust pointers
   90|  56.0k|        buf += 8;
   91|  56.0k|        utf8_output += 8;
   92|  56.0k|        in = nextin;
   93|   763k|      } else {
   94|       |        // 1. pack the bytes
   95|       |        // obviously suboptimal.
   96|   763k|        const __m128i utf8_packed = _mm_packus_epi16(in, nextin);
   97|       |        // 2. store (16 bytes)
   98|   763k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   99|       |        // 3. adjust pointers
  100|   763k|        buf += 16;
  101|   763k|        utf8_output += 16;
  102|   763k|        continue; // we are done for this round!
  103|   763k|      }
  104|   819k|    }
  105|       |
  106|       |    // no bits set above 7th bit
  107|   374k|    const __m128i one_byte_bytemask =
  108|   374k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000);
  109|   374k|    const uint16_t one_byte_bitmask =
  110|   374k|        static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
  111|       |
  112|       |    // no bits set above 11th bit
  113|   374k|    const __m128i one_or_two_bytes_bytemask =
  114|   374k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000);
  115|   374k|    const uint16_t one_or_two_bytes_bitmask =
  116|   374k|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  117|       |
  118|   374k|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (118:9): [True: 233k, False: 140k]
  ------------------
  119|   233k|      internal::westmere::write_v_u16_11bits_to_utf8(
  120|   233k|          in, utf8_output, one_byte_bytemask, one_byte_bitmask);
  121|   233k|      buf += 8;
  122|   233k|      continue;
  123|   233k|    }
  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|   140k|    const __m128i surrogates_bytemask =
  129|   140k|        _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|   140k|    const uint16_t surrogates_bitmask =
  134|   140k|        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|   140k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (137:9): [True: 131k, False: 9.08k]
  ------------------
  138|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  139|   131k|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  140|   131k|                                              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|   131k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  170|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  171|   131k|      const __m128i t0 = _mm_shuffle_epi8(in, dup_even);
  172|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  173|   131k|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  169|   131k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  174|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  175|   131k|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  169|   131k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  176|       |
  177|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  178|   131k|      const __m128i s0 = _mm_srli_epi16(in, 4);
  179|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  180|   131k|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  169|   131k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  181|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  182|   131k|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  169|   131k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  183|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  184|   131k|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  169|   131k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  185|   131k|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  186|   131k|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  169|   131k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  187|   131k|      const __m128i s4 = _mm_xor_si128(s3, m0);
  188|   131k|#undef simdutf_vec
  189|       |
  190|       |      // 4. expand code units 16-bit => 32-bit
  191|   131k|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  192|   131k|      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|   131k|      const uint16_t mask =
  196|   131k|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  197|   131k|      if (mask == 0) {
  ------------------
  |  Branch (197:11): [True: 22.1k, False: 109k]
  ------------------
  198|       |        // We only have three-byte code units. Use fast path.
  199|  22.1k|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  200|  22.1k|                                              15, 13, -1, -1, -1, -1);
  201|  22.1k|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  202|  22.1k|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  203|  22.1k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  204|  22.1k|        utf8_output += 12;
  205|  22.1k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  206|  22.1k|        utf8_output += 12;
  207|  22.1k|        buf += 8;
  208|  22.1k|        continue;
  209|  22.1k|      }
  210|   109k|      const uint8_t mask0 = uint8_t(mask);
  211|       |
  212|   109k|      const uint8_t *row0 =
  213|   109k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  214|   109k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  215|   109k|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  216|       |
  217|   109k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  218|       |
  219|   109k|      const uint8_t *row1 =
  220|   109k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  221|   109k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  222|   109k|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  223|       |
  224|   109k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  225|   109k|      utf8_output += row0[0];
  226|   109k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  227|   109k|      utf8_output += row1[0];
  228|       |
  229|   109k|      buf += 8;
  230|       |      // surrogate pair(s) in a register
  231|   109k|    } 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|  9.08k|      size_t forward = 15;
  236|  9.08k|      size_t k = 0;
  237|  9.08k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (237:11): [True: 0, False: 9.08k]
  ------------------
  238|      0|        forward = size_t(end - buf - 1);
  239|      0|      }
  240|   134k|      for (; k < forward; k++) {
  ------------------
  |  Branch (240:14): [True: 125k, False: 8.97k]
  ------------------
  241|   125k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  242|   125k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (242:13): [True: 16.3k, False: 108k]
  ------------------
  243|  16.3k|          *utf8_output++ = char(word);
  244|   108k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (244:20): [True: 4.59k, False: 104k]
  ------------------
  245|  4.59k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  246|  4.59k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  247|   104k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (247:20): [True: 94.0k, False: 10.2k]
  ------------------
  248|  94.0k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  249|  94.0k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  250|  94.0k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  251|  94.0k|        } 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: 106, False: 10.1k]
  ------------------
  259|    106|            return std::make_pair(nullptr, utf8_output);
  260|    106|          }
  261|  10.1k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  262|  10.1k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  263|  10.1k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  264|  10.1k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  265|  10.1k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  266|  10.1k|        }
  267|   125k|      }
  268|  8.97k|      buf += k;
  269|  8.97k|    }
  270|   140k|  } // while
  271|       |
  272|    732|  return std::make_pair(buf, utf8_output);
  273|    838|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_125sse_convert_utf16_to_utf8ILNS_10endiannessE1EEENSt3__14pairIPKDsPcEES7_mS8_:
   56|    742|sse_convert_utf16_to_utf8(const char16_t *buf, size_t len, char *utf8_output) {
   57|       |
   58|    742|  const char16_t *end = buf + len;
   59|       |
   60|    742|  const __m128i v_0000 = _mm_setzero_si128();
   61|    742|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
   62|    742|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
   63|    742|  const size_t safety_margin =
   64|    742|      12; // to avoid overruns, see issue
   65|       |          // https://github.com/simdutf/simdutf/issues/92
   66|       |
   67|  1.04M|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (67:10): [True: 1.04M, False: 658]
  ------------------
   68|  1.04M|    __m128i in = _mm_loadu_si128((__m128i *)buf);
   69|  1.04M|    if (big_endian) {
  ------------------
  |  Branch (69:9): [True: 1.04M, Folded]
  ------------------
   70|  1.04M|      const __m128i swap =
   71|  1.04M|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   72|  1.04M|      in = _mm_shuffle_epi8(in, swap);
   73|  1.04M|    }
   74|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
   75|  1.04M|    const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80);
   76|  1.04M|    if (_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (76:9): [True: 650k, False: 393k]
  ------------------
   77|   650k|      __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
   78|   650k|      if (big_endian) {
  ------------------
  |  Branch (78:11): [True: 650k, Folded]
  ------------------
   79|   650k|        const __m128i swap =
   80|   650k|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   81|   650k|        nextin = _mm_shuffle_epi8(nextin, swap);
   82|   650k|      }
   83|   650k|      if (!_mm_testz_si128(nextin, v_ff80)) {
  ------------------
  |  Branch (83:11): [True: 35.9k, False: 614k]
  ------------------
   84|       |        // 1. pack the bytes
   85|       |        // obviously suboptimal.
   86|  35.9k|        const __m128i utf8_packed = _mm_packus_epi16(in, in);
   87|       |        // 2. store (16 bytes)
   88|  35.9k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   89|       |        // 3. adjust pointers
   90|  35.9k|        buf += 8;
   91|  35.9k|        utf8_output += 8;
   92|  35.9k|        in = nextin;
   93|   614k|      } else {
   94|       |        // 1. pack the bytes
   95|       |        // obviously suboptimal.
   96|   614k|        const __m128i utf8_packed = _mm_packus_epi16(in, nextin);
   97|       |        // 2. store (16 bytes)
   98|   614k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   99|       |        // 3. adjust pointers
  100|   614k|        buf += 16;
  101|   614k|        utf8_output += 16;
  102|   614k|        continue; // we are done for this round!
  103|   614k|      }
  104|   650k|    }
  105|       |
  106|       |    // no bits set above 7th bit
  107|   429k|    const __m128i one_byte_bytemask =
  108|   429k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000);
  109|   429k|    const uint16_t one_byte_bitmask =
  110|   429k|        static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
  111|       |
  112|       |    // no bits set above 11th bit
  113|   429k|    const __m128i one_or_two_bytes_bytemask =
  114|   429k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000);
  115|   429k|    const uint16_t one_or_two_bytes_bitmask =
  116|   429k|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  117|       |
  118|   429k|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (118:9): [True: 32.2k, False: 397k]
  ------------------
  119|  32.2k|      internal::westmere::write_v_u16_11bits_to_utf8(
  120|  32.2k|          in, utf8_output, one_byte_bytemask, one_byte_bitmask);
  121|  32.2k|      buf += 8;
  122|  32.2k|      continue;
  123|  32.2k|    }
  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|   397k|    const __m128i surrogates_bytemask =
  129|   397k|        _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|   397k|    const uint16_t surrogates_bitmask =
  134|   397k|        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|   397k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (137:9): [True: 392k, False: 4.58k]
  ------------------
  138|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  139|   392k|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  140|   392k|                                              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|   392k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  170|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  171|   392k|      const __m128i t0 = _mm_shuffle_epi8(in, dup_even);
  172|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  173|   392k|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  169|   392k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  174|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  175|   392k|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  169|   392k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  176|       |
  177|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  178|   392k|      const __m128i s0 = _mm_srli_epi16(in, 4);
  179|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  180|   392k|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  169|   392k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  181|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  182|   392k|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  169|   392k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  183|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  184|   392k|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  169|   392k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  185|   392k|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  186|   392k|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  169|   392k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  187|   392k|      const __m128i s4 = _mm_xor_si128(s3, m0);
  188|   392k|#undef simdutf_vec
  189|       |
  190|       |      // 4. expand code units 16-bit => 32-bit
  191|   392k|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  192|   392k|      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|   392k|      const uint16_t mask =
  196|   392k|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  197|   392k|      if (mask == 0) {
  ------------------
  |  Branch (197:11): [True: 249k, False: 143k]
  ------------------
  198|       |        // We only have three-byte code units. Use fast path.
  199|   249k|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  200|   249k|                                              15, 13, -1, -1, -1, -1);
  201|   249k|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  202|   249k|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  203|   249k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  204|   249k|        utf8_output += 12;
  205|   249k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  206|   249k|        utf8_output += 12;
  207|   249k|        buf += 8;
  208|   249k|        continue;
  209|   249k|      }
  210|   143k|      const uint8_t mask0 = uint8_t(mask);
  211|       |
  212|   143k|      const uint8_t *row0 =
  213|   143k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  214|   143k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  215|   143k|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  216|       |
  217|   143k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  218|       |
  219|   143k|      const uint8_t *row1 =
  220|   143k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  221|   143k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  222|   143k|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  223|       |
  224|   143k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  225|   143k|      utf8_output += row0[0];
  226|   143k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  227|   143k|      utf8_output += row1[0];
  228|       |
  229|   143k|      buf += 8;
  230|       |      // surrogate pair(s) in a register
  231|   143k|    } 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|  4.58k|      size_t forward = 15;
  236|  4.58k|      size_t k = 0;
  237|  4.58k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (237:11): [True: 0, False: 4.58k]
  ------------------
  238|      0|        forward = size_t(end - buf - 1);
  239|      0|      }
  240|  67.3k|      for (; k < forward; k++) {
  ------------------
  |  Branch (240:14): [True: 62.8k, False: 4.50k]
  ------------------
  241|  62.8k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  242|  62.8k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (242:13): [True: 10.4k, False: 52.4k]
  ------------------
  243|  10.4k|          *utf8_output++ = char(word);
  244|  52.4k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (244:20): [True: 1.96k, False: 50.4k]
  ------------------
  245|  1.96k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  246|  1.96k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  247|  50.4k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (247:20): [True: 45.2k, False: 5.16k]
  ------------------
  248|  45.2k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  249|  45.2k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  250|  45.2k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  251|  45.2k|        } else {
  252|       |          // must be a surrogate pair
  253|  5.16k|          uint16_t diff = uint16_t(word - 0xD800);
  254|  5.16k|          uint16_t next_word =
  255|  5.16k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  256|  5.16k|          k++;
  257|  5.16k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  258|  5.16k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (258:15): [True: 84, False: 5.07k]
  ------------------
  259|     84|            return std::make_pair(nullptr, utf8_output);
  260|     84|          }
  261|  5.07k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  262|  5.07k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  263|  5.07k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  264|  5.07k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  265|  5.07k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  266|  5.07k|        }
  267|  62.8k|      }
  268|  4.50k|      buf += k;
  269|  4.50k|    }
  270|   397k|  } // while
  271|       |
  272|    658|  return std::make_pair(buf, utf8_output);
  273|    742|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_137sse_convert_utf16_to_utf8_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
  285|    678|                                      char *utf8_output) {
  286|    678|  const char16_t *start = buf;
  287|    678|  const char16_t *end = buf + len;
  288|       |
  289|    678|  const __m128i v_0000 = _mm_setzero_si128();
  290|    678|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
  291|    678|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
  292|    678|  const size_t safety_margin =
  293|    678|      12; // to avoid overruns, see issue
  294|       |          // https://github.com/simdutf/simdutf/issues/92
  295|       |
  296|   703k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (296:10): [True: 702k, False: 518]
  ------------------
  297|   702k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
  298|   702k|    if (big_endian) {
  ------------------
  |  Branch (298:9): [Folded, False: 702k]
  ------------------
  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|   702k|    const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80);
  305|   702k|    if (_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (305:9): [True: 279k, False: 423k]
  ------------------
  306|   279k|      __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
  307|   279k|      if (big_endian) {
  ------------------
  |  Branch (307:11): [Folded, False: 279k]
  ------------------
  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|   279k|      if (!_mm_testz_si128(nextin, v_ff80)) {
  ------------------
  |  Branch (312:11): [True: 9.53k, False: 269k]
  ------------------
  313|       |        // 1. pack the bytes
  314|       |        // obviously suboptimal.
  315|  9.53k|        const __m128i utf8_packed = _mm_packus_epi16(in, in);
  316|       |        // 2. store (16 bytes)
  317|  9.53k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  318|       |        // 3. adjust pointers
  319|  9.53k|        buf += 8;
  320|  9.53k|        utf8_output += 8;
  321|  9.53k|        in = nextin;
  322|   269k|      } else {
  323|       |        // 1. pack the bytes
  324|       |        // obviously suboptimal.
  325|   269k|        const __m128i utf8_packed = _mm_packus_epi16(in, nextin);
  326|       |        // 2. store (16 bytes)
  327|   269k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  328|       |        // 3. adjust pointers
  329|   269k|        buf += 16;
  330|   269k|        utf8_output += 16;
  331|   269k|        continue; // we are done for this round!
  332|   269k|      }
  333|   279k|    }
  334|       |
  335|       |    // no bits set above 7th bit
  336|   433k|    const __m128i one_byte_bytemask =
  337|   433k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000);
  338|   433k|    const uint16_t one_byte_bitmask =
  339|   433k|        static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
  340|       |
  341|       |    // no bits set above 11th bit
  342|   433k|    const __m128i one_or_two_bytes_bytemask =
  343|   433k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000);
  344|   433k|    const uint16_t one_or_two_bytes_bitmask =
  345|   433k|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  346|       |
  347|   433k|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (347:9): [True: 181k, False: 251k]
  ------------------
  348|   181k|      internal::westmere::write_v_u16_11bits_to_utf8(
  349|   181k|          in, utf8_output, one_byte_bytemask, one_byte_bitmask);
  350|   181k|      buf += 8;
  351|   181k|      continue;
  352|   181k|    }
  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|   251k|    const __m128i surrogates_bytemask =
  358|   251k|        _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|   251k|    const uint16_t surrogates_bitmask =
  363|   251k|        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|   251k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (366:9): [True: 249k, False: 1.83k]
  ------------------
  367|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  368|   249k|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  369|   249k|                                              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|   249k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  399|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  400|   249k|      const __m128i t0 = _mm_shuffle_epi8(in, dup_even);
  401|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  402|   249k|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  398|   249k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  403|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  404|   249k|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  398|   249k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  405|       |
  406|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  407|   249k|      const __m128i s0 = _mm_srli_epi16(in, 4);
  408|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  409|   249k|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  398|   249k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  410|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  411|   249k|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  398|   249k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  412|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  413|   249k|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  398|   249k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  414|   249k|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  415|   249k|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  398|   249k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  416|   249k|      const __m128i s4 = _mm_xor_si128(s3, m0);
  417|   249k|#undef simdutf_vec
  418|       |
  419|       |      // 4. expand code units 16-bit => 32-bit
  420|   249k|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  421|   249k|      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|   249k|      const uint16_t mask =
  425|   249k|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  426|   249k|      if (mask == 0) {
  ------------------
  |  Branch (426:11): [True: 179k, False: 70.3k]
  ------------------
  427|       |        // We only have three-byte code units. Use fast path.
  428|   179k|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  429|   179k|                                              15, 13, -1, -1, -1, -1);
  430|   179k|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  431|   179k|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  432|   179k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  433|   179k|        utf8_output += 12;
  434|   179k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  435|   179k|        utf8_output += 12;
  436|   179k|        buf += 8;
  437|   179k|        continue;
  438|   179k|      }
  439|  70.3k|      const uint8_t mask0 = uint8_t(mask);
  440|       |
  441|  70.3k|      const uint8_t *row0 =
  442|  70.3k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  443|  70.3k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  444|  70.3k|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  445|       |
  446|  70.3k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  447|       |
  448|  70.3k|      const uint8_t *row1 =
  449|  70.3k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  450|  70.3k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  451|  70.3k|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  452|       |
  453|  70.3k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  454|  70.3k|      utf8_output += row0[0];
  455|  70.3k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  456|  70.3k|      utf8_output += row1[0];
  457|       |
  458|  70.3k|      buf += 8;
  459|       |      // surrogate pair(s) in a register
  460|  70.3k|    } 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|  1.83k|      size_t forward = 15;
  465|  1.83k|      size_t k = 0;
  466|  1.83k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (466:11): [True: 0, False: 1.83k]
  ------------------
  467|      0|        forward = size_t(end - buf - 1);
  468|      0|      }
  469|  25.4k|      for (; k < forward; k++) {
  ------------------
  |  Branch (469:14): [True: 23.8k, False: 1.67k]
  ------------------
  470|  23.8k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  471|  23.8k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (471:13): [True: 4.87k, False: 18.9k]
  ------------------
  472|  4.87k|          *utf8_output++ = char(word);
  473|  18.9k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (473:20): [True: 1.36k, False: 17.5k]
  ------------------
  474|  1.36k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  475|  1.36k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  476|  17.5k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (476:20): [True: 15.3k, False: 2.24k]
  ------------------
  477|  15.3k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  478|  15.3k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  479|  15.3k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  480|  15.3k|        } else {
  481|       |          // must be a surrogate pair
  482|  2.24k|          uint16_t diff = uint16_t(word - 0xD800);
  483|  2.24k|          uint16_t next_word =
  484|  2.24k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  485|  2.24k|          k++;
  486|  2.24k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  487|  2.24k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (487:15): [True: 160, False: 2.08k]
  ------------------
  488|    160|            return std::make_pair(
  489|    160|                result(error_code::SURROGATE, buf - start + k - 1),
  490|    160|                utf8_output);
  491|    160|          }
  492|  2.08k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  493|  2.08k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  494|  2.08k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  495|  2.08k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  496|  2.08k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  497|  2.08k|        }
  498|  23.8k|      }
  499|  1.67k|      buf += k;
  500|  1.67k|    }
  501|   251k|  } // while
  502|       |
  503|    518|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  504|    678|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_137sse_convert_utf16_to_utf8_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPcEEPKDsmS7_:
  285|    716|                                      char *utf8_output) {
  286|    716|  const char16_t *start = buf;
  287|    716|  const char16_t *end = buf + len;
  288|       |
  289|    716|  const __m128i v_0000 = _mm_setzero_si128();
  290|    716|  const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
  291|    716|  const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
  292|    716|  const size_t safety_margin =
  293|    716|      12; // to avoid overruns, see issue
  294|       |          // https://github.com/simdutf/simdutf/issues/92
  295|       |
  296|   937k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (296:10): [True: 936k, False: 538]
  ------------------
  297|   936k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
  298|   936k|    if (big_endian) {
  ------------------
  |  Branch (298:9): [True: 936k, Folded]
  ------------------
  299|   936k|      const __m128i swap =
  300|   936k|          _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  301|   936k|      in = _mm_shuffle_epi8(in, swap);
  302|   936k|    }
  303|       |    // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
  304|   936k|    const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80);
  305|   936k|    if (_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (305:9): [True: 617k, False: 319k]
  ------------------
  306|   617k|      __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
  307|   617k|      if (big_endian) {
  ------------------
  |  Branch (307:11): [True: 617k, Folded]
  ------------------
  308|   617k|        const __m128i swap =
  309|   617k|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  310|   617k|        nextin = _mm_shuffle_epi8(nextin, swap);
  311|   617k|      }
  312|   617k|      if (!_mm_testz_si128(nextin, v_ff80)) {
  ------------------
  |  Branch (312:11): [True: 89.1k, False: 527k]
  ------------------
  313|       |        // 1. pack the bytes
  314|       |        // obviously suboptimal.
  315|  89.1k|        const __m128i utf8_packed = _mm_packus_epi16(in, in);
  316|       |        // 2. store (16 bytes)
  317|  89.1k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  318|       |        // 3. adjust pointers
  319|  89.1k|        buf += 8;
  320|  89.1k|        utf8_output += 8;
  321|  89.1k|        in = nextin;
  322|   527k|      } else {
  323|       |        // 1. pack the bytes
  324|       |        // obviously suboptimal.
  325|   527k|        const __m128i utf8_packed = _mm_packus_epi16(in, nextin);
  326|       |        // 2. store (16 bytes)
  327|   527k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  328|       |        // 3. adjust pointers
  329|   527k|        buf += 16;
  330|   527k|        utf8_output += 16;
  331|   527k|        continue; // we are done for this round!
  332|   527k|      }
  333|   617k|    }
  334|       |
  335|       |    // no bits set above 7th bit
  336|   408k|    const __m128i one_byte_bytemask =
  337|   408k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000);
  338|   408k|    const uint16_t one_byte_bitmask =
  339|   408k|        static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
  340|       |
  341|       |    // no bits set above 11th bit
  342|   408k|    const __m128i one_or_two_bytes_bytemask =
  343|   408k|        _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000);
  344|   408k|    const uint16_t one_or_two_bytes_bitmask =
  345|   408k|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  346|       |
  347|   408k|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (347:9): [True: 111k, False: 296k]
  ------------------
  348|   111k|      internal::westmere::write_v_u16_11bits_to_utf8(
  349|   111k|          in, utf8_output, one_byte_bytemask, one_byte_bitmask);
  350|   111k|      buf += 8;
  351|   111k|      continue;
  352|   111k|    }
  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|   296k|    const __m128i surrogates_bytemask =
  358|   296k|        _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|   296k|    const uint16_t surrogates_bitmask =
  363|   296k|        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|   296k|    if (surrogates_bitmask == 0x0000) {
  ------------------
  |  Branch (366:9): [True: 272k, False: 24.2k]
  ------------------
  367|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  368|   272k|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  369|   272k|                                              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|   272k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  399|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  400|   272k|      const __m128i t0 = _mm_shuffle_epi8(in, dup_even);
  401|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  402|   272k|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  398|   272k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  403|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  404|   272k|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  398|   272k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  405|       |
  406|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  407|   272k|      const __m128i s0 = _mm_srli_epi16(in, 4);
  408|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  409|   272k|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  398|   272k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  410|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  411|   272k|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  398|   272k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  412|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  413|   272k|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  398|   272k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  414|   272k|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  415|   272k|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  398|   272k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  416|   272k|      const __m128i s4 = _mm_xor_si128(s3, m0);
  417|   272k|#undef simdutf_vec
  418|       |
  419|       |      // 4. expand code units 16-bit => 32-bit
  420|   272k|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  421|   272k|      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|   272k|      const uint16_t mask =
  425|   272k|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  426|   272k|      if (mask == 0) {
  ------------------
  |  Branch (426:11): [True: 53.8k, False: 218k]
  ------------------
  427|       |        // We only have three-byte code units. Use fast path.
  428|  53.8k|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  429|  53.8k|                                              15, 13, -1, -1, -1, -1);
  430|  53.8k|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  431|  53.8k|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  432|  53.8k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  433|  53.8k|        utf8_output += 12;
  434|  53.8k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  435|  53.8k|        utf8_output += 12;
  436|  53.8k|        buf += 8;
  437|  53.8k|        continue;
  438|  53.8k|      }
  439|   218k|      const uint8_t mask0 = uint8_t(mask);
  440|       |
  441|   218k|      const uint8_t *row0 =
  442|   218k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  443|   218k|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  444|   218k|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  445|       |
  446|   218k|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  447|       |
  448|   218k|      const uint8_t *row1 =
  449|   218k|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  450|   218k|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  451|   218k|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  452|       |
  453|   218k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  454|   218k|      utf8_output += row0[0];
  455|   218k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  456|   218k|      utf8_output += row1[0];
  457|       |
  458|   218k|      buf += 8;
  459|       |      // surrogate pair(s) in a register
  460|   218k|    } 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|  24.2k|      size_t forward = 15;
  465|  24.2k|      size_t k = 0;
  466|  24.2k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (466:11): [True: 0, False: 24.2k]
  ------------------
  467|      0|        forward = size_t(end - buf - 1);
  468|      0|      }
  469|   361k|      for (; k < forward; k++) {
  ------------------
  |  Branch (469:14): [True: 336k, False: 24.1k]
  ------------------
  470|   336k|        uint16_t word = scalar::utf16::swap_if_needed<big_endian>(buf[k]);
  471|   336k|        if ((word & 0xFF80) == 0) {
  ------------------
  |  Branch (471:13): [True: 134k, False: 202k]
  ------------------
  472|   134k|          *utf8_output++ = char(word);
  473|   202k|        } else if ((word & 0xF800) == 0) {
  ------------------
  |  Branch (473:20): [True: 14.8k, False: 187k]
  ------------------
  474|  14.8k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  475|  14.8k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  476|   187k|        } else if ((word & 0xF800) != 0xD800) {
  ------------------
  |  Branch (476:20): [True: 161k, False: 26.4k]
  ------------------
  477|   161k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  478|   161k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  479|   161k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  480|   161k|        } else {
  481|       |          // must be a surrogate pair
  482|  26.4k|          uint16_t diff = uint16_t(word - 0xD800);
  483|  26.4k|          uint16_t next_word =
  484|  26.4k|              scalar::utf16::swap_if_needed<big_endian>(buf[k + 1]);
  485|  26.4k|          k++;
  486|  26.4k|          uint16_t diff2 = uint16_t(next_word - 0xDC00);
  487|  26.4k|          if ((diff | diff2) > 0x3FF) {
  ------------------
  |  Branch (487:15): [True: 178, False: 26.2k]
  ------------------
  488|    178|            return std::make_pair(
  489|    178|                result(error_code::SURROGATE, buf - start + k - 1),
  490|    178|                utf8_output);
  491|    178|          }
  492|  26.2k|          uint32_t value = (diff << 10) + diff2 + 0x10000;
  493|  26.2k|          *utf8_output++ = char((value >> 18) | 0b11110000);
  494|  26.2k|          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
  495|  26.2k|          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
  496|  26.2k|          *utf8_output++ = char((value & 0b111111) | 0b10000000);
  497|  26.2k|        }
  498|   336k|      }
  499|  24.1k|      buf += k;
  500|  24.1k|    }
  501|   296k|  } // while
  502|       |
  503|    538|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  504|    716|}

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

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_126sse_convert_utf32_to_utf16ILNS_10endiannessE0EEENSt3__14pairIPKDiPDsEES7_mS8_:
   58|    738|                           char16_t *utf16_output) {
   59|       |
   60|    738|  const char32_t *end = buf + len;
   61|       |
   62|    738|  const __m128i v_ffff0000 = _mm_set1_epi32((int32_t)0xffff0000);
   63|    738|  __m128i forbidden_bytemask = _mm_setzero_si128();
   64|       |
   65|  76.2k|  while (end - buf >= 16 + 8) {
  ------------------
  |  Branch (65:10): [True: 75.5k, False: 714]
  ------------------
   66|  75.5k|    const __m128i *ptr = reinterpret_cast<const __m128i *>(buf);
   67|  75.5k|    const __m128i in0 = _mm_loadu_si128(ptr + 0);
   68|  75.5k|    const __m128i in1 = _mm_loadu_si128(ptr + 1);
   69|  75.5k|    const __m128i in2 = _mm_loadu_si128(ptr + 2);
   70|  75.5k|    const __m128i in3 = _mm_loadu_si128(ptr + 3);
   71|       |
   72|  75.5k|    const __m128i combined =
   73|  75.5k|        _mm_or_si128(_mm_or_si128(in2, in3), _mm_or_si128(in0, in1));
   74|  75.5k|    if (simdutf_likely(_mm_testz_si128(combined, v_ffff0000))) {
  ------------------
  |  |   89|  75.5k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (89:31): [True: 74.2k, False: 1.26k]
  |  |  ------------------
  ------------------
   75|       |      // No bits set above 16th, directly pack UTF-32 to UTF-16
   76|  74.2k|      __m128i utf16_packed0 = _mm_packus_epi32(in0, in1);
   77|  74.2k|      __m128i utf16_packed1 = _mm_packus_epi32(in2, in3);
   78|       |
   79|  74.2k|      const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800);
   80|  74.2k|      const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
   81|  74.2k|      forbidden_bytemask = _mm_or_si128(
   82|  74.2k|          forbidden_bytemask,
   83|  74.2k|          _mm_or_si128(
   84|  74.2k|              _mm_cmpeq_epi16(_mm_and_si128(utf16_packed0, v_f800), v_d800),
   85|  74.2k|              _mm_cmpeq_epi16(_mm_and_si128(utf16_packed1, v_f800), v_d800)));
   86|       |
   87|  74.2k|      if (big_endian) {
  ------------------
  |  Branch (87:11): [Folded, False: 74.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|  74.2k|      _mm_storeu_si128((__m128i *)utf16_output + 0, utf16_packed0);
   95|  74.2k|      _mm_storeu_si128((__m128i *)utf16_output + 1, utf16_packed1);
   96|  74.2k|      utf16_output += 16;
   97|  74.2k|      buf += 16;
   98|  74.2k|    } else {
   99|  1.26k|      if (!validate_utf32(in0, in1) || !validate_utf32(in2, in3)) {
  ------------------
  |  Branch (99:11): [True: 18, False: 1.24k]
  |  Branch (99:40): [True: 6, False: 1.24k]
  ------------------
  100|     24|        return std::make_pair(nullptr, utf16_output);
  101|     24|      }
  102|       |
  103|  1.24k|      const auto ret0 = sse_expand_surrogate<big_endian>(in0);
  104|  1.24k|      _mm_storeu_si128((__m128i *)utf16_output, ret0.compressed);
  105|  1.24k|      utf16_output += ret0.u16count;
  106|       |
  107|  1.24k|      const auto ret1 = sse_expand_surrogate<big_endian>(in1);
  108|  1.24k|      _mm_storeu_si128((__m128i *)utf16_output, ret1.compressed);
  109|  1.24k|      utf16_output += ret1.u16count;
  110|       |
  111|  1.24k|      const auto ret2 = sse_expand_surrogate<big_endian>(in2);
  112|  1.24k|      _mm_storeu_si128((__m128i *)utf16_output, ret2.compressed);
  113|  1.24k|      utf16_output += ret2.u16count;
  114|       |
  115|  1.24k|      const auto ret3 = sse_expand_surrogate<big_endian>(in3);
  116|  1.24k|      _mm_storeu_si128((__m128i *)utf16_output, ret3.compressed);
  117|  1.24k|      utf16_output += ret3.u16count;
  118|       |
  119|  1.24k|      buf += 16;
  120|  1.24k|    }
  121|  75.5k|  }
  122|       |
  123|       |  // check for invalid input
  124|    714|  if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (124:7): [True: 18, False: 696]
  ------------------
  125|     18|    return std::make_pair(nullptr, utf16_output);
  126|     18|  }
  127|       |
  128|    696|  return std::make_pair(buf, utf16_output);
  129|    714|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_114validate_utf32EDv2_xS2_:
   37|  5.01k|simdutf_really_inline bool validate_utf32(const __m128i a, const __m128i b) {
   38|  5.01k|  using vector_u32 = simd32<uint32_t>;
   39|       |
   40|  5.01k|  const auto in0 = vector_u32(a);
   41|  5.01k|  const auto in1 = vector_u32(b);
   42|       |
   43|  5.01k|  const auto standardmax = vector_u32::splat(0x10ffff);
   44|  5.01k|  const auto offset = vector_u32::splat(0xffff2000);
   45|  5.01k|  const auto standardoffsetmax = vector_u32::splat(0xfffff7ff);
   46|       |
   47|  5.01k|  const auto too_large = max(in0, in1) > standardmax;
   48|  5.01k|  const auto surrogate0 = (in0 + offset) > standardoffsetmax;
   49|  5.01k|  const auto surrogate1 = (in1 + offset) > standardoffsetmax;
   50|       |
   51|  5.01k|  const auto combined = too_large | surrogate0 | surrogate1;
   52|  5.01k|  return !combined.any();
   53|  5.01k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_120sse_expand_surrogateILNS_10endiannessE0EEENS1_18expansion_result_tEDv2_x:
    9|  4.96k|expansion_result_t sse_expand_surrogate(const __m128i x) {
   10|  4.96k|  using vector_u32 = simd32<uint32_t>;
   11|  4.96k|  using vector_u8 = simd8<uint8_t>;
   12|       |
   13|  4.96k|  const auto in = vector_u32(x);
   14|       |
   15|  4.96k|  const auto non_surrogate_mask = (in & uint32_t(0xffff0000)) == uint32_t(0);
   16|  4.96k|  const auto mask = (~non_surrogate_mask.to_4bit_bitmask()) & 0xf;
   17|       |
   18|  4.96k|  const auto t0 = in - uint32_t(0x00010000);
   19|  4.96k|  const auto hi = t0.shr<10>() & uint32_t(0x000003ff);
   20|  4.96k|  const auto lo = t0.shl<16>() & uint32_t(0x03ff0000);
   21|  4.96k|  const auto surrogates = (lo | hi) | uint32_t(0xdc00d800);
   22|       |
   23|  4.96k|  const auto merged = as_vector_u8(select(non_surrogate_mask, in, surrogates));
   24|       |
   25|  4.96k|  const auto shuffle = vector_u8::load(
   26|  4.96k|      (byte_order == endianness::LITTLE)
  ------------------
  |  Branch (26:7): [True: 4.96k, Folded]
  ------------------
   27|  4.96k|          ? tables::utf32_to_utf16::pack_utf32_to_utf16le[mask]
   28|  4.96k|          : tables::utf32_to_utf16::pack_utf32_to_utf16be[mask]);
   29|       |
   30|  4.96k|  const size_t u16count = (4 + count_ones(mask));
   31|  4.96k|  const auto compressed = shuffle.lookup_16(merged);
   32|       |
   33|  4.96k|  return {u16count, compressed};
   34|  4.96k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_126sse_convert_utf32_to_utf16ILNS_10endiannessE1EEENSt3__14pairIPKDiPDsEES7_mS8_:
   58|    634|                           char16_t *utf16_output) {
   59|       |
   60|    634|  const char32_t *end = buf + len;
   61|       |
   62|    634|  const __m128i v_ffff0000 = _mm_set1_epi32((int32_t)0xffff0000);
   63|    634|  __m128i forbidden_bytemask = _mm_setzero_si128();
   64|       |
   65|  13.4k|  while (end - buf >= 16 + 8) {
  ------------------
  |  Branch (65:10): [True: 12.9k, False: 540]
  ------------------
   66|  12.9k|    const __m128i *ptr = reinterpret_cast<const __m128i *>(buf);
   67|  12.9k|    const __m128i in0 = _mm_loadu_si128(ptr + 0);
   68|  12.9k|    const __m128i in1 = _mm_loadu_si128(ptr + 1);
   69|  12.9k|    const __m128i in2 = _mm_loadu_si128(ptr + 2);
   70|  12.9k|    const __m128i in3 = _mm_loadu_si128(ptr + 3);
   71|       |
   72|  12.9k|    const __m128i combined =
   73|  12.9k|        _mm_or_si128(_mm_or_si128(in2, in3), _mm_or_si128(in0, in1));
   74|  12.9k|    if (simdutf_likely(_mm_testz_si128(combined, v_ffff0000))) {
  ------------------
  |  |   89|  12.9k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (89:31): [True: 11.6k, False: 1.28k]
  |  |  ------------------
  ------------------
   75|       |      // No bits set above 16th, directly pack UTF-32 to UTF-16
   76|  11.6k|      __m128i utf16_packed0 = _mm_packus_epi32(in0, in1);
   77|  11.6k|      __m128i utf16_packed1 = _mm_packus_epi32(in2, in3);
   78|       |
   79|  11.6k|      const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800);
   80|  11.6k|      const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
   81|  11.6k|      forbidden_bytemask = _mm_or_si128(
   82|  11.6k|          forbidden_bytemask,
   83|  11.6k|          _mm_or_si128(
   84|  11.6k|              _mm_cmpeq_epi16(_mm_and_si128(utf16_packed0, v_f800), v_d800),
   85|  11.6k|              _mm_cmpeq_epi16(_mm_and_si128(utf16_packed1, v_f800), v_d800)));
   86|       |
   87|  11.6k|      if (big_endian) {
  ------------------
  |  Branch (87:11): [True: 11.6k, Folded]
  ------------------
   88|  11.6k|        const __m128i swap =
   89|  11.6k|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   90|  11.6k|        utf16_packed0 = _mm_shuffle_epi8(utf16_packed0, swap);
   91|  11.6k|        utf16_packed1 = _mm_shuffle_epi8(utf16_packed1, swap);
   92|  11.6k|      }
   93|       |
   94|  11.6k|      _mm_storeu_si128((__m128i *)utf16_output + 0, utf16_packed0);
   95|  11.6k|      _mm_storeu_si128((__m128i *)utf16_output + 1, utf16_packed1);
   96|  11.6k|      utf16_output += 16;
   97|  11.6k|      buf += 16;
   98|  11.6k|    } else {
   99|  1.28k|      if (!validate_utf32(in0, in1) || !validate_utf32(in2, in3)) {
  ------------------
  |  Branch (99:11): [True: 70, False: 1.21k]
  |  Branch (99:40): [True: 24, False: 1.19k]
  ------------------
  100|     94|        return std::make_pair(nullptr, utf16_output);
  101|     94|      }
  102|       |
  103|  1.19k|      const auto ret0 = sse_expand_surrogate<big_endian>(in0);
  104|  1.19k|      _mm_storeu_si128((__m128i *)utf16_output, ret0.compressed);
  105|  1.19k|      utf16_output += ret0.u16count;
  106|       |
  107|  1.19k|      const auto ret1 = sse_expand_surrogate<big_endian>(in1);
  108|  1.19k|      _mm_storeu_si128((__m128i *)utf16_output, ret1.compressed);
  109|  1.19k|      utf16_output += ret1.u16count;
  110|       |
  111|  1.19k|      const auto ret2 = sse_expand_surrogate<big_endian>(in2);
  112|  1.19k|      _mm_storeu_si128((__m128i *)utf16_output, ret2.compressed);
  113|  1.19k|      utf16_output += ret2.u16count;
  114|       |
  115|  1.19k|      const auto ret3 = sse_expand_surrogate<big_endian>(in3);
  116|  1.19k|      _mm_storeu_si128((__m128i *)utf16_output, ret3.compressed);
  117|  1.19k|      utf16_output += ret3.u16count;
  118|       |
  119|  1.19k|      buf += 16;
  120|  1.19k|    }
  121|  12.9k|  }
  122|       |
  123|       |  // check for invalid input
  124|    540|  if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (124:7): [True: 12, False: 528]
  ------------------
  125|     12|    return std::make_pair(nullptr, utf16_output);
  126|     12|  }
  127|       |
  128|    528|  return std::make_pair(buf, utf16_output);
  129|    540|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_120sse_expand_surrogateILNS_10endiannessE1EEENS1_18expansion_result_tEDv2_x:
    9|  4.76k|expansion_result_t sse_expand_surrogate(const __m128i x) {
   10|  4.76k|  using vector_u32 = simd32<uint32_t>;
   11|  4.76k|  using vector_u8 = simd8<uint8_t>;
   12|       |
   13|  4.76k|  const auto in = vector_u32(x);
   14|       |
   15|  4.76k|  const auto non_surrogate_mask = (in & uint32_t(0xffff0000)) == uint32_t(0);
   16|  4.76k|  const auto mask = (~non_surrogate_mask.to_4bit_bitmask()) & 0xf;
   17|       |
   18|  4.76k|  const auto t0 = in - uint32_t(0x00010000);
   19|  4.76k|  const auto hi = t0.shr<10>() & uint32_t(0x000003ff);
   20|  4.76k|  const auto lo = t0.shl<16>() & uint32_t(0x03ff0000);
   21|  4.76k|  const auto surrogates = (lo | hi) | uint32_t(0xdc00d800);
   22|       |
   23|  4.76k|  const auto merged = as_vector_u8(select(non_surrogate_mask, in, surrogates));
   24|       |
   25|  4.76k|  const auto shuffle = vector_u8::load(
   26|  4.76k|      (byte_order == endianness::LITTLE)
  ------------------
  |  Branch (26:7): [Folded, False: 4.76k]
  ------------------
   27|  4.76k|          ? tables::utf32_to_utf16::pack_utf32_to_utf16le[mask]
   28|  4.76k|          : tables::utf32_to_utf16::pack_utf32_to_utf16be[mask]);
   29|       |
   30|  4.76k|  const size_t u16count = (4 + count_ones(mask));
   31|  4.76k|  const auto compressed = shuffle.lookup_16(merged);
   32|       |
   33|  4.76k|  return {u16count, compressed};
   34|  4.76k|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_138sse_convert_utf32_to_utf16_with_errorsILNS_10endiannessE0EEENSt3__14pairINS_6resultEPDsEEPKDimS7_:
  134|    658|                                       char16_t *utf16_output) {
  135|    658|  const char32_t *start = buf;
  136|    658|  const char32_t *end = buf + len;
  137|       |
  138|    658|  const __m128i v_ffff0000 = _mm_set1_epi32((int32_t)0xffff0000);
  139|       |
  140|  56.3k|  while (end - buf >= 8) {
  ------------------
  |  Branch (140:10): [True: 56.0k, False: 298]
  ------------------
  141|  56.0k|    const __m128i in = _mm_loadu_si128((__m128i *)buf);
  142|  56.0k|    const __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
  143|       |
  144|  56.0k|    const __m128i combined = _mm_or_si128(in, nextin);
  145|  56.0k|    if (simdutf_likely(_mm_testz_si128(combined, v_ffff0000))) {
  ------------------
  |  |   89|  56.0k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (89:31): [True: 53.4k, False: 2.60k]
  |  |  ------------------
  ------------------
  146|       |      // No bits set above 16th, directly pack UTF-32 to UTF-16
  147|  53.4k|      __m128i utf16_packed = _mm_packus_epi32(in, nextin);
  148|       |
  149|  53.4k|      const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800);
  150|  53.4k|      const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
  151|  53.4k|      const __m128i forbidden_bytemask =
  152|  53.4k|          _mm_cmpeq_epi16(_mm_and_si128(utf16_packed, v_f800), v_d800);
  153|  53.4k|      if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (153:11): [True: 48, False: 53.4k]
  ------------------
  154|     48|        return std::make_pair(result(error_code::SURROGATE, buf - start),
  155|     48|                              utf16_output);
  156|     48|      }
  157|       |
  158|  53.4k|      if (big_endian) {
  ------------------
  |  Branch (158:11): [Folded, False: 53.4k]
  ------------------
  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|  53.4k|      _mm_storeu_si128((__m128i *)utf16_output, utf16_packed);
  165|  53.4k|      utf16_output += 8;
  166|  53.4k|      buf += 8;
  167|  53.4k|    } else {
  168|  2.60k|      size_t forward = 7;
  169|  2.60k|      size_t k = 0;
  170|  2.60k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (170:11): [True: 0, False: 2.60k]
  ------------------
  171|      0|        forward = size_t(end - buf - 1);
  172|      0|      }
  173|  19.3k|      for (; k < forward; k++) {
  ------------------
  |  Branch (173:14): [True: 17.0k, False: 2.29k]
  ------------------
  174|  17.0k|        uint32_t word = buf[k];
  175|  17.0k|        if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (175:13): [True: 8.47k, False: 8.55k]
  ------------------
  176|       |          // will not generate a surrogate pair
  177|  8.47k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (177:15): [True: 866, False: 7.60k]
  |  Branch (177:33): [True: 30, False: 836]
  ------------------
  178|     30|            return std::make_pair(
  179|     30|                result(error_code::SURROGATE, buf - start + k), utf16_output);
  180|     30|          }
  181|  8.44k|          *utf16_output++ =
  182|  8.44k|              big_endian
  ------------------
  |  Branch (182:15): [Folded, False: 8.44k]
  ------------------
  183|  8.44k|                  ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8))
  184|  8.44k|                  : char16_t(word);
  185|  8.55k|        } else {
  186|       |          // will generate a surrogate pair
  187|  8.55k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (187:15): [True: 282, False: 8.27k]
  ------------------
  188|    282|            return std::make_pair(
  189|    282|                result(error_code::TOO_LARGE, buf - start + k), utf16_output);
  190|    282|          }
  191|  8.27k|          word -= 0x10000;
  192|  8.27k|          uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
  193|  8.27k|          uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
  194|  8.27k|          if (big_endian) {
  ------------------
  |  Branch (194:15): [Folded, False: 8.27k]
  ------------------
  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|  8.27k|          *utf16_output++ = char16_t(high_surrogate);
  201|  8.27k|          *utf16_output++ = char16_t(low_surrogate);
  202|  8.27k|        }
  203|  17.0k|      }
  204|  2.29k|      buf += k;
  205|  2.29k|    }
  206|  56.0k|  }
  207|       |
  208|    298|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf16_output);
  209|    658|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_138sse_convert_utf32_to_utf16_with_errorsILNS_10endiannessE1EEENSt3__14pairINS_6resultEPDsEEPKDimS7_:
  134|    592|                                       char16_t *utf16_output) {
  135|    592|  const char32_t *start = buf;
  136|    592|  const char32_t *end = buf + len;
  137|       |
  138|    592|  const __m128i v_ffff0000 = _mm_set1_epi32((int32_t)0xffff0000);
  139|       |
  140|  3.46k|  while (end - buf >= 8) {
  ------------------
  |  Branch (140:10): [True: 3.17k, False: 292]
  ------------------
  141|  3.17k|    const __m128i in = _mm_loadu_si128((__m128i *)buf);
  142|  3.17k|    const __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
  143|       |
  144|  3.17k|    const __m128i combined = _mm_or_si128(in, nextin);
  145|  3.17k|    if (simdutf_likely(_mm_testz_si128(combined, v_ffff0000))) {
  ------------------
  |  |   89|  3.17k|    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
  |  |  ------------------
  |  |  |  Branch (89:31): [True: 2.28k, False: 896]
  |  |  ------------------
  ------------------
  146|       |      // No bits set above 16th, directly pack UTF-32 to UTF-16
  147|  2.28k|      __m128i utf16_packed = _mm_packus_epi32(in, nextin);
  148|       |
  149|  2.28k|      const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800);
  150|  2.28k|      const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
  151|  2.28k|      const __m128i forbidden_bytemask =
  152|  2.28k|          _mm_cmpeq_epi16(_mm_and_si128(utf16_packed, v_f800), v_d800);
  153|  2.28k|      if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (153:11): [True: 18, False: 2.26k]
  ------------------
  154|     18|        return std::make_pair(result(error_code::SURROGATE, buf - start),
  155|     18|                              utf16_output);
  156|     18|      }
  157|       |
  158|  2.26k|      if (big_endian) {
  ------------------
  |  Branch (158:11): [True: 2.26k, Folded]
  ------------------
  159|  2.26k|        const __m128i swap =
  160|  2.26k|            _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
  161|  2.26k|        utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
  162|  2.26k|      }
  163|       |
  164|  2.26k|      _mm_storeu_si128((__m128i *)utf16_output, utf16_packed);
  165|  2.26k|      utf16_output += 8;
  166|  2.26k|      buf += 8;
  167|  2.26k|    } else {
  168|    896|      size_t forward = 7;
  169|    896|      size_t k = 0;
  170|    896|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (170:11): [True: 0, False: 896]
  ------------------
  171|      0|        forward = size_t(end - buf - 1);
  172|      0|      }
  173|  5.77k|      for (; k < forward; k++) {
  ------------------
  |  Branch (173:14): [True: 5.15k, False: 614]
  ------------------
  174|  5.15k|        uint32_t word = buf[k];
  175|  5.15k|        if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (175:13): [True: 2.81k, False: 2.34k]
  ------------------
  176|       |          // will not generate a surrogate pair
  177|  2.81k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (177:15): [True: 732, False: 2.08k]
  |  Branch (177:33): [True: 28, False: 704]
  ------------------
  178|     28|            return std::make_pair(
  179|     28|                result(error_code::SURROGATE, buf - start + k), utf16_output);
  180|     28|          }
  181|  2.78k|          *utf16_output++ =
  182|  2.78k|              big_endian
  ------------------
  |  Branch (182:15): [True: 2.78k, Folded]
  ------------------
  183|  2.78k|                  ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8))
  184|  2.78k|                  : char16_t(word);
  185|  2.78k|        } else {
  186|       |          // will generate a surrogate pair
  187|  2.34k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (187:15): [True: 254, False: 2.08k]
  ------------------
  188|    254|            return std::make_pair(
  189|    254|                result(error_code::TOO_LARGE, buf - start + k), utf16_output);
  190|    254|          }
  191|  2.08k|          word -= 0x10000;
  192|  2.08k|          uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
  193|  2.08k|          uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
  194|  2.08k|          if (big_endian) {
  ------------------
  |  Branch (194:15): [True: 2.08k, Folded]
  ------------------
  195|  2.08k|            high_surrogate =
  196|  2.08k|                uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
  197|  2.08k|            low_surrogate =
  198|  2.08k|                uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
  199|  2.08k|          }
  200|  2.08k|          *utf16_output++ = char16_t(high_surrogate);
  201|  2.08k|          *utf16_output++ = char16_t(low_surrogate);
  202|  2.08k|        }
  203|  5.15k|      }
  204|    614|      buf += k;
  205|    614|    }
  206|  3.17k|  }
  207|       |
  208|    292|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf16_output);
  209|    592|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_125sse_convert_utf32_to_utf8EPKDimPc:
    2|    860|sse_convert_utf32_to_utf8(const char32_t *buf, size_t len, char *utf8_output) {
    3|    860|  const char32_t *end = buf + len;
    4|       |
    5|    860|  const __m128i v_0000 = _mm_setzero_si128();              //__m128 = 128 bits
    6|    860|  const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800); // 1111 1000 0000
    7|       |                                                           // 0000
    8|    860|  const __m128i v_c080 = _mm_set1_epi16((uint16_t)0xc080); // 1100 0000 1000
    9|       |                                                           // 0000
   10|    860|  const __m128i v_ff80 = _mm_set1_epi16((uint16_t)0xff80); // 1111 1111 1000
   11|       |                                                           // 0000
   12|    860|  const __m128i v_ffff0000 = _mm_set1_epi32(
   13|    860|      (uint32_t)0xffff0000); // 1111 1111 1111 1111 0000 0000 0000 0000
   14|    860|  const __m128i v_7fffffff = _mm_set1_epi32(
   15|    860|      (uint32_t)0x7fffffff); // 0111 1111 1111 1111 1111 1111 1111 1111
   16|    860|  __m128i running_max = _mm_setzero_si128();
   17|    860|  __m128i forbidden_bytemask = _mm_setzero_si128();
   18|    860|  const size_t safety_margin =
   19|    860|      12; // to avoid overruns, see issue
   20|       |          // https://github.com/simdutf/simdutf/issues/92
   21|       |
   22|  36.1k|  while (end - buf >=
  ------------------
  |  Branch (22:10): [True: 35.4k, False: 686]
  ------------------
   23|  36.1k|         std::ptrdiff_t(
   24|  36.1k|             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|  35.4k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
   29|  35.4k|    __m128i nextin = _mm_loadu_si128(
   30|  35.4k|        (__m128i *)buf + 1); // These two values can hold only 8 UTF32 chars
   31|  35.4k|    running_max = _mm_max_epu32(
   32|  35.4k|        _mm_max_epu32(in, running_max), // take element-wise max char32_t from
   33|       |                                        // in and running_max vector
   34|  35.4k|        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|  35.4k|    __m128i in_16 = _mm_packus_epi32(
   40|  35.4k|        _mm_and_si128(in, v_7fffffff),
   41|  35.4k|        _mm_and_si128(
   42|  35.4k|            nextin,
   43|  35.4k|            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|  35.4k|    if (_mm_testz_si128(in_16, v_ff80)) { // if the first two blocks are ASCII
  ------------------
  |  Branch (60:9): [True: 31.1k, False: 4.31k]
  ------------------
   61|  31.1k|      __m128i thirdin = _mm_loadu_si128((__m128i *)buf + 2);
   62|  31.1k|      __m128i fourthin = _mm_loadu_si128((__m128i *)buf + 3);
   63|  31.1k|      running_max = _mm_max_epu32(
   64|  31.1k|          _mm_max_epu32(thirdin, running_max),
   65|  31.1k|          fourthin); // take the running max of all 4 vectors thus far
   66|  31.1k|      __m128i nextin_16 = _mm_packus_epi32(
   67|  31.1k|          _mm_and_si128(thirdin, v_7fffffff),
   68|  31.1k|          _mm_and_si128(fourthin,
   69|  31.1k|                        v_7fffffff)); // pack into 1 vector, now you have two
   70|  31.1k|      if (!_mm_testz_si128(
  ------------------
  |  Branch (70:11): [True: 770, False: 30.3k]
  ------------------
   71|  31.1k|              nextin_16,
   72|  31.1k|              v_ff80)) { // checks if the second packed vector is ASCII, if not:
   73|       |        // 1. pack the bytes
   74|       |        // obviously suboptimal.
   75|    770|        const __m128i utf8_packed = _mm_packus_epi16(
   76|    770|            in_16, in_16); // creates two copy of in_16 in 1 vector
   77|       |        // 2. store (16 bytes)
   78|    770|        _mm_storeu_si128((__m128i *)utf8_output,
   79|    770|                         utf8_packed); // put them into the output
   80|       |        // 3. adjust pointers
   81|    770|        buf += 8; // the char32_t buffer pointer goes up 8 char32_t chars* 32
   82|       |                  // bits =  256 bits
   83|    770|        utf8_output +=
   84|    770|            8; // same with output, e.g. lift the first two blocks alone.
   85|       |        // Proceed with next input
   86|    770|        in_16 = nextin_16;
   87|       |        // We need to update in and nextin because they are used later.
   88|    770|        in = thirdin;
   89|    770|        nextin = fourthin;
   90|  30.3k|      } else {
   91|       |        // 1. pack the bytes
   92|  30.3k|        const __m128i utf8_packed = _mm_packus_epi16(in_16, nextin_16);
   93|       |        // 2. store (16 bytes)
   94|  30.3k|        _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
   95|       |        // 3. adjust pointers
   96|  30.3k|        buf += 16;
   97|  30.3k|        utf8_output += 16;
   98|  30.3k|        continue; // we are done for this round!
   99|  30.3k|      }
  100|  31.1k|    }
  101|       |
  102|       |    // no bits set above 7th bit -- find out all the ASCII characters
  103|  5.08k|    const __m128i one_byte_bytemask =
  104|  5.08k|        _mm_cmpeq_epi16( // this takes four bytes at a time and compares:
  105|  5.08k|            _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|  5.08k|            v_0000                        //
  108|  5.08k|        ); // 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|  5.08k|    const uint16_t one_byte_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(
  113|  5.08k|        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|  5.08k|    const __m128i one_or_two_bytes_bytemask =
  118|  5.08k|        _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_0000);
  119|  5.08k|    const uint16_t one_or_two_bytes_bitmask =
  120|  5.08k|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  121|       |
  122|  5.08k|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (122:9): [True: 2.42k, False: 2.66k]
  ------------------
  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|  2.42k|      const __m128i v_1f00 =
  129|  2.42k|          _mm_set1_epi16((int16_t)0x1f00); // 0001 1111 0000 0000
  130|  2.42k|      const __m128i v_003f =
  131|  2.42k|          _mm_set1_epi16((int16_t)0x003f); // 0000 0000 0011 1111
  132|       |
  133|       |      // t0 = [000a|aaaa|bbbb|bb00]
  134|  2.42k|      const __m128i t0 = _mm_slli_epi16(in_16, 2); // shift packed vector by two
  135|       |      // t1 = [000a|aaaa|0000|0000]
  136|  2.42k|      const __m128i t1 = _mm_and_si128(t0, v_1f00); // potential first utf8 byte
  137|       |      // t2 = [0000|0000|00bb|bbbb]
  138|  2.42k|      const __m128i t2 =
  139|  2.42k|          _mm_and_si128(in_16, v_003f); // potential second utf8 byte
  140|       |      // t3 = [000a|aaaa|00bb|bbbb]
  141|  2.42k|      const __m128i t3 =
  142|  2.42k|          _mm_or_si128(t1, t2); // first and second potential utf8 byte together
  143|       |      // t4 = [110a|aaaa|10bb|bbbb]
  144|  2.42k|      const __m128i t4 = _mm_or_si128(
  145|  2.42k|          t3,
  146|  2.42k|          v_c080); // t3 | 1100 0000 1000 0000 = full potential 2-byte utf8 unit
  147|       |
  148|       |      // 2. merge ASCII and 2-byte codewords
  149|  2.42k|      const __m128i utf8_unpacked =
  150|  2.42k|          _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|  2.42k|      const uint16_t m0 = one_byte_bitmask & 0x5555; // m0 = 0h0g0f0e0d0c0b0a
  156|  2.42k|      const uint16_t m1 =
  157|  2.42k|          static_cast<uint16_t>(m0 >> 7); // m1 = 00000000h0g0f0e0
  158|  2.42k|      const uint8_t m2 =
  159|  2.42k|          static_cast<uint8_t>((m0 | m1) & 0xff); // m2 =         hdgcfbea
  160|       |      // 4. pack the bytes
  161|  2.42k|      const uint8_t *row =
  162|  2.42k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0];
  163|  2.42k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  164|  2.42k|      const __m128i utf8_packed = _mm_shuffle_epi8(utf8_unpacked, shuffle);
  165|       |
  166|       |      // 5. store bytes
  167|  2.42k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  168|       |
  169|       |      // 6. adjust pointers
  170|  2.42k|      buf += 8;
  171|  2.42k|      utf8_output += row[0];
  172|  2.42k|      continue;
  173|  2.42k|    }
  174|       |
  175|       |    // Check for overflow in packing
  176|       |
  177|  2.66k|    const __m128i saturation_bytemask = _mm_cmpeq_epi32(
  178|  2.66k|        _mm_and_si128(_mm_or_si128(in, nextin), v_ffff0000), v_0000);
  179|  2.66k|    const uint32_t saturation_bitmask =
  180|  2.66k|        static_cast<uint32_t>(_mm_movemask_epi8(saturation_bytemask));
  181|  2.66k|    if (saturation_bitmask == 0xffff) {
  ------------------
  |  Branch (181:9): [True: 1.31k, False: 1.34k]
  ------------------
  182|       |      // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
  183|  1.31k|      const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
  184|  1.31k|      forbidden_bytemask =
  185|  1.31k|          _mm_or_si128(forbidden_bytemask,
  186|  1.31k|                       _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_d800));
  187|       |
  188|  1.31k|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  189|  1.31k|                                              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.31k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  219|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  220|  1.31k|      const __m128i t0 = _mm_shuffle_epi8(in_16, dup_even);
  221|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  222|  1.31k|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  218|  1.31k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  223|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  224|  1.31k|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  218|  1.31k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  225|       |
  226|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  227|  1.31k|      const __m128i s0 = _mm_srli_epi16(in_16, 4);
  228|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  229|  1.31k|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  218|  1.31k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  230|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  231|  1.31k|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  218|  1.31k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  232|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  233|  1.31k|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  218|  1.31k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  234|  1.31k|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  235|  1.31k|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  218|  1.31k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  236|  1.31k|      const __m128i s4 = _mm_xor_si128(s3, m0);
  237|  1.31k|#undef simdutf_vec
  238|       |
  239|       |      // 4. expand code units 16-bit => 32-bit
  240|  1.31k|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  241|  1.31k|      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.31k|      const uint16_t mask =
  245|  1.31k|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  246|  1.31k|      if (mask == 0) {
  ------------------
  |  Branch (246:11): [True: 620, False: 696]
  ------------------
  247|       |        // We only have three-byte code units. Use fast path.
  248|    620|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  249|    620|                                              15, 13, -1, -1, -1, -1);
  250|    620|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  251|    620|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  252|    620|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  253|    620|        utf8_output += 12;
  254|    620|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  255|    620|        utf8_output += 12;
  256|    620|        buf += 8;
  257|    620|        continue;
  258|    620|      }
  259|    696|      const uint8_t mask0 = uint8_t(mask);
  260|       |
  261|    696|      const uint8_t *row0 =
  262|    696|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  263|    696|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  264|    696|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  265|       |
  266|    696|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  267|       |
  268|    696|      const uint8_t *row1 =
  269|    696|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  270|    696|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  271|    696|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  272|       |
  273|    696|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  274|    696|      utf8_output += row0[0];
  275|    696|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  276|    696|      utf8_output += row1[0];
  277|       |
  278|    696|      buf += 8;
  279|  1.34k|    } 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.34k|      size_t forward = 15;
  285|  1.34k|      size_t k = 0;
  286|  1.34k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (286:11): [True: 0, False: 1.34k]
  ------------------
  287|      0|        forward = size_t(end - buf - 1);
  288|      0|      }
  289|  19.6k|      for (; k < forward; k++) {
  ------------------
  |  Branch (289:14): [True: 18.4k, False: 1.17k]
  ------------------
  290|  18.4k|        uint32_t word = buf[k];
  291|  18.4k|        if ((word & 0xFFFFFF80) == 0) {
  ------------------
  |  Branch (291:13): [True: 9.51k, False: 8.93k]
  ------------------
  292|  9.51k|          *utf8_output++ = char(word);
  293|  9.51k|        } else if ((word & 0xFFFFF800) == 0) {
  ------------------
  |  Branch (293:20): [True: 1.14k, False: 7.79k]
  ------------------
  294|  1.14k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  295|  1.14k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  296|  7.79k|        } else if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (296:20): [True: 3.01k, False: 4.77k]
  ------------------
  297|  3.01k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (297:15): [True: 1.00k, False: 2.01k]
  |  Branch (297:33): [True: 24, False: 980]
  ------------------
  298|     24|            return std::make_pair(nullptr, utf8_output);
  299|     24|          }
  300|  2.99k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  301|  2.99k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  302|  2.99k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  303|  4.77k|        } else {
  304|  4.77k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (304:15): [True: 150, False: 4.62k]
  ------------------
  305|    150|            return std::make_pair(nullptr, utf8_output);
  306|    150|          }
  307|  4.62k|          *utf8_output++ = char((word >> 18) | 0b11110000);
  308|  4.62k|          *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
  309|  4.62k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  310|  4.62k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  311|  4.62k|        }
  312|  18.4k|      }
  313|  1.17k|      buf += k;
  314|  1.17k|    }
  315|  2.66k|  } // while
  316|       |
  317|       |  // check for invalid input
  318|    686|  const __m128i v_10ffff = _mm_set1_epi32((uint32_t)0x10ffff);
  319|    686|  if (static_cast<uint16_t>(_mm_movemask_epi8(_mm_cmpeq_epi32(
  ------------------
  |  Branch (319:7): [True: 18, False: 668]
  ------------------
  320|    686|          _mm_max_epu32(running_max, v_10ffff), v_10ffff))) != 0xffff) {
  321|     18|    return std::make_pair(nullptr, utf8_output);
  322|     18|  }
  323|       |
  324|    668|  if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (324:7): [True: 54, False: 614]
  ------------------
  325|     54|    return std::make_pair(nullptr, utf8_output);
  326|     54|  }
  327|       |
  328|    614|  return std::make_pair(buf, utf8_output);
  329|    668|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_137sse_convert_utf32_to_utf8_with_errorsEPKDimPc:
  333|    892|                                      char *utf8_output) {
  334|    892|  const char32_t *end = buf + len;
  335|    892|  const char32_t *start = buf;
  336|       |
  337|    892|  const __m128i v_0000 = _mm_setzero_si128();
  338|    892|  const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800);
  339|    892|  const __m128i v_c080 = _mm_set1_epi16((uint16_t)0xc080);
  340|    892|  const __m128i v_ff80 = _mm_set1_epi16((uint16_t)0xff80);
  341|    892|  const __m128i v_ffff0000 = _mm_set1_epi32((uint32_t)0xffff0000);
  342|    892|  const __m128i v_7fffffff = _mm_set1_epi32((uint32_t)0x7fffffff);
  343|    892|  const __m128i v_10ffff = _mm_set1_epi32((uint32_t)0x10ffff);
  344|       |
  345|    892|  const size_t safety_margin =
  346|    892|      12; // to avoid overruns, see issue
  347|       |          // https://github.com/simdutf/simdutf/issues/92
  348|       |
  349|  64.9k|  while (end - buf >= std::ptrdiff_t(16 + safety_margin)) {
  ------------------
  |  Branch (349:10): [True: 64.3k, False: 686]
  ------------------
  350|       |    // We load two 16 bytes registers for a total of 32 bytes or 8 characters.
  351|  64.3k|    __m128i in = _mm_loadu_si128((__m128i *)buf);
  352|  64.3k|    __m128i nextin = _mm_loadu_si128((__m128i *)buf + 1);
  353|       |    // Check for too large input
  354|  64.3k|    __m128i max_input = _mm_max_epu32(_mm_max_epu32(in, nextin), v_10ffff);
  355|  64.3k|    if (static_cast<uint16_t>(_mm_movemask_epi8(
  ------------------
  |  Branch (355:9): [True: 62, False: 64.2k]
  ------------------
  356|  64.3k|            _mm_cmpeq_epi32(max_input, v_10ffff))) != 0xffff) {
  357|     62|      return std::make_pair(result(error_code::TOO_LARGE, buf - start),
  358|     62|                            utf8_output);
  359|     62|    }
  360|       |
  361|       |    // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned
  362|       |    // saturation
  363|  64.2k|    __m128i in_16 = _mm_packus_epi32(_mm_and_si128(in, v_7fffffff),
  364|  64.2k|                                     _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|  64.2k|    if (_mm_testz_si128(in_16, v_ff80)) { // ASCII fast path!!!!
  ------------------
  |  Branch (369:9): [True: 56.1k, False: 8.14k]
  ------------------
  370|       |      // 1. pack the bytes
  371|       |      // obviously suboptimal.
  372|  56.1k|      const __m128i utf8_packed = _mm_packus_epi16(in_16, in_16);
  373|       |      // 2. store (16 bytes)
  374|  56.1k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  375|       |      // 3. adjust pointers
  376|  56.1k|      buf += 8;
  377|  56.1k|      utf8_output += 8;
  378|  56.1k|      continue;
  379|  56.1k|    }
  380|       |
  381|       |    // no bits set above 7th bit
  382|  8.14k|    const __m128i one_byte_bytemask =
  383|  8.14k|        _mm_cmpeq_epi16(_mm_and_si128(in_16, v_ff80), v_0000);
  384|  8.14k|    const uint16_t one_byte_bitmask =
  385|  8.14k|        static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
  386|       |
  387|       |    // no bits set above 11th bit
  388|  8.14k|    const __m128i one_or_two_bytes_bytemask =
  389|  8.14k|        _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_0000);
  390|  8.14k|    const uint16_t one_or_two_bytes_bitmask =
  391|  8.14k|        static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
  392|       |
  393|  8.14k|    if (one_or_two_bytes_bitmask == 0xffff) {
  ------------------
  |  Branch (393:9): [True: 4.92k, False: 3.22k]
  ------------------
  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|  4.92k|      const __m128i v_1f00 = _mm_set1_epi16((int16_t)0x1f00);
  400|  4.92k|      const __m128i v_003f = _mm_set1_epi16((int16_t)0x003f);
  401|       |
  402|       |      // t0 = [000a|aaaa|bbbb|bb00]
  403|  4.92k|      const __m128i t0 = _mm_slli_epi16(in_16, 2);
  404|       |      // t1 = [000a|aaaa|0000|0000]
  405|  4.92k|      const __m128i t1 = _mm_and_si128(t0, v_1f00);
  406|       |      // t2 = [0000|0000|00bb|bbbb]
  407|  4.92k|      const __m128i t2 = _mm_and_si128(in_16, v_003f);
  408|       |      // t3 = [000a|aaaa|00bb|bbbb]
  409|  4.92k|      const __m128i t3 = _mm_or_si128(t1, t2);
  410|       |      // t4 = [110a|aaaa|10bb|bbbb]
  411|  4.92k|      const __m128i t4 = _mm_or_si128(t3, v_c080);
  412|       |
  413|       |      // 2. merge ASCII and 2-byte codewords
  414|  4.92k|      const __m128i utf8_unpacked =
  415|  4.92k|          _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|  4.92k|      const uint16_t m0 = one_byte_bitmask & 0x5555; // m0 = 0h0g0f0e0d0c0b0a
  421|  4.92k|      const uint16_t m1 =
  422|  4.92k|          static_cast<uint16_t>(m0 >> 7); // m1 = 00000000h0g0f0e0
  423|  4.92k|      const uint8_t m2 =
  424|  4.92k|          static_cast<uint8_t>((m0 | m1) & 0xff); // m2 =         hdgcfbea
  425|       |      // 4. pack the bytes
  426|  4.92k|      const uint8_t *row =
  427|  4.92k|          &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0];
  428|  4.92k|      const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
  429|  4.92k|      const __m128i utf8_packed = _mm_shuffle_epi8(utf8_unpacked, shuffle);
  430|       |
  431|       |      // 5. store bytes
  432|  4.92k|      _mm_storeu_si128((__m128i *)utf8_output, utf8_packed);
  433|       |
  434|       |      // 6. adjust pointers
  435|  4.92k|      buf += 8;
  436|  4.92k|      utf8_output += row[0];
  437|  4.92k|      continue;
  438|  4.92k|    }
  439|       |
  440|       |    // Check for overflow in packing
  441|  3.22k|    const __m128i saturation_bytemask = _mm_cmpeq_epi32(
  442|  3.22k|        _mm_and_si128(_mm_or_si128(in, nextin), v_ffff0000), v_0000);
  443|  3.22k|    const uint32_t saturation_bitmask =
  444|  3.22k|        static_cast<uint32_t>(_mm_movemask_epi8(saturation_bytemask));
  445|       |
  446|  3.22k|    if (saturation_bitmask == 0xffff) {
  ------------------
  |  Branch (446:9): [True: 1.66k, False: 1.55k]
  ------------------
  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.66k|      const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
  451|  1.66k|      const __m128i forbidden_bytemask =
  452|  1.66k|          _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_d800);
  453|  1.66k|      if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) {
  ------------------
  |  Branch (453:11): [True: 32, False: 1.63k]
  ------------------
  454|     32|        return std::make_pair(result(error_code::SURROGATE, buf - start),
  455|     32|                              utf8_output);
  456|     32|      }
  457|       |
  458|  1.63k|      const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
  459|  1.63k|                                              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.63k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  489|       |      // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
  490|  1.63k|      const __m128i t0 = _mm_shuffle_epi8(in_16, dup_even);
  491|       |      // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
  492|  1.63k|      const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
  ------------------
  |  |  488|  1.63k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  493|       |      // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
  494|  1.63k|      const __m128i t2 = _mm_or_si128(t1, simdutf_vec(0b1000000000000000));
  ------------------
  |  |  488|  1.63k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  495|       |
  496|       |      // [aaaa|bbbb|bbcc|cccc] =>  [0000|aaaa|bbbb|bbcc]
  497|  1.63k|      const __m128i s0 = _mm_srli_epi16(in_16, 4);
  498|       |      // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
  499|  1.63k|      const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
  ------------------
  |  |  488|  1.63k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  500|       |      // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
  501|  1.63k|      const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
  ------------------
  |  |  488|  1.63k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  502|       |      // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
  503|  1.63k|      const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
  ------------------
  |  |  488|  1.63k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  504|  1.63k|      const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask,
  505|  1.63k|                                          simdutf_vec(0b0100000000000000));
  ------------------
  |  |  488|  1.63k|#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
  ------------------
  506|  1.63k|      const __m128i s4 = _mm_xor_si128(s3, m0);
  507|  1.63k|#undef simdutf_vec
  508|       |
  509|       |      // 4. expand code units 16-bit => 32-bit
  510|  1.63k|      const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
  511|  1.63k|      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.63k|      const uint16_t mask =
  515|  1.63k|          (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa);
  516|  1.63k|      if (mask == 0) {
  ------------------
  |  Branch (516:11): [True: 868, False: 766]
  ------------------
  517|       |        // We only have three-byte code units. Use fast path.
  518|    868|        const __m128i shuffle = _mm_setr_epi8(2, 3, 1, 6, 7, 5, 10, 11, 9, 14,
  519|    868|                                              15, 13, -1, -1, -1, -1);
  520|    868|        const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
  521|    868|        const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
  522|    868|        _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  523|    868|        utf8_output += 12;
  524|    868|        _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  525|    868|        utf8_output += 12;
  526|    868|        buf += 8;
  527|    868|        continue;
  528|    868|      }
  529|    766|      const uint8_t mask0 = uint8_t(mask);
  530|       |
  531|    766|      const uint8_t *row0 =
  532|    766|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
  533|    766|      const __m128i shuffle0 = _mm_loadu_si128((__m128i *)(row0 + 1));
  534|    766|      const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
  535|       |
  536|    766|      const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
  537|       |
  538|    766|      const uint8_t *row1 =
  539|    766|          &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
  540|    766|      const __m128i shuffle1 = _mm_loadu_si128((__m128i *)(row1 + 1));
  541|    766|      const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
  542|       |
  543|    766|      _mm_storeu_si128((__m128i *)utf8_output, utf8_0);
  544|    766|      utf8_output += row0[0];
  545|    766|      _mm_storeu_si128((__m128i *)utf8_output, utf8_1);
  546|    766|      utf8_output += row1[0];
  547|       |
  548|    766|      buf += 8;
  549|  1.55k|    } 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.55k|      size_t forward = 15;
  555|  1.55k|      size_t k = 0;
  556|  1.55k|      if (size_t(end - buf) < forward + 1) {
  ------------------
  |  Branch (556:11): [True: 0, False: 1.55k]
  ------------------
  557|      0|        forward = size_t(end - buf - 1);
  558|      0|      }
  559|  24.2k|      for (; k < forward; k++) {
  ------------------
  |  Branch (559:14): [True: 22.8k, False: 1.44k]
  ------------------
  560|  22.8k|        uint32_t word = buf[k];
  561|  22.8k|        if ((word & 0xFFFFFF80) == 0) {
  ------------------
  |  Branch (561:13): [True: 8.97k, False: 13.8k]
  ------------------
  562|  8.97k|          *utf8_output++ = char(word);
  563|  13.8k|        } else if ((word & 0xFFFFF800) == 0) {
  ------------------
  |  Branch (563:20): [True: 1.70k, False: 12.1k]
  ------------------
  564|  1.70k|          *utf8_output++ = char((word >> 6) | 0b11000000);
  565|  1.70k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  566|  12.1k|        } else if ((word & 0xFFFF0000) == 0) {
  ------------------
  |  Branch (566:20): [True: 6.87k, False: 5.29k]
  ------------------
  567|  6.87k|          if (word >= 0xD800 && word <= 0xDFFF) {
  ------------------
  |  Branch (567:15): [True: 3.97k, False: 2.90k]
  |  Branch (567:33): [True: 24, False: 3.94k]
  ------------------
  568|     24|            return std::make_pair(
  569|     24|                result(error_code::SURROGATE, buf - start + k), utf8_output);
  570|     24|          }
  571|  6.85k|          *utf8_output++ = char((word >> 12) | 0b11100000);
  572|  6.85k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  573|  6.85k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  574|  6.85k|        } else {
  575|  5.29k|          if (word > 0x10FFFF) {
  ------------------
  |  Branch (575:15): [True: 88, False: 5.20k]
  ------------------
  576|     88|            return std::make_pair(
  577|     88|                result(error_code::TOO_LARGE, buf - start + k), utf8_output);
  578|     88|          }
  579|  5.20k|          *utf8_output++ = char((word >> 18) | 0b11110000);
  580|  5.20k|          *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
  581|  5.20k|          *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
  582|  5.20k|          *utf8_output++ = char((word & 0b111111) | 0b10000000);
  583|  5.20k|        }
  584|  22.8k|      }
  585|  1.44k|      buf += k;
  586|  1.44k|    }
  587|  3.22k|  } // while
  588|    686|  return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
  589|    892|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_129convert_masked_utf8_to_latin1EPKcmRPc:
    9|   613k|                                     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|   613k|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   21|   613k|  const uint16_t input_utf8_end_of_code_point_mask =
   22|   613k|      utf8_end_of_code_point_mask &
   23|   613k|      0xfff; // we are only processing 12 bytes in case it is not all ASCII
   24|   613k|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (24:7): [True: 3.33k, False: 610k]
  ------------------
   25|       |    // We process the data in chunks of 12 bytes.
   26|  3.33k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(latin1_output), in);
   27|  3.33k|    latin1_output += 12; // We wrote 12 characters.
   28|  3.33k|    return 12;           // We consumed 12 bytes.
   29|  3.33k|  }
   30|       |  /// We do not have a fast path available, so we fallback.
   31|   610k|  const uint8_t idx =
   32|   610k|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
   33|   610k|  const uint8_t consumed =
   34|   610k|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
   35|       |  // this indicates an invalid input:
   36|   610k|  if (idx >= 64) {
  ------------------
  |  Branch (36:7): [True: 33.5k, False: 577k]
  ------------------
   37|  33.5k|    return consumed;
   38|  33.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|   577k|  const __m128i sh =
   46|   577k|      _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
   47|   577k|  const __m128i perm = _mm_shuffle_epi8(in, sh);
   48|   577k|  const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   49|   577k|  const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   50|   577k|  __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   51|   577k|  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|   577k|  _mm_storel_epi64((__m128i *)latin1_output, latin1_packed);
   56|   577k|  latin1_output += 6; // We wrote 6 bytes.
   57|   577k|  return consumed;
   58|   610k|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_128convert_masked_utf8_to_utf16ILNS_10endiannessE0EEEmPKcmRPDs:
   10|  3.03M|                                    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|  3.03M|  const __m128i swap =
   23|  3.03M|      _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   24|  3.03M|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   25|  3.03M|  const uint16_t input_utf8_end_of_code_point_mask =
   26|  3.03M|      utf8_end_of_code_point_mask & 0xfff;
   27|  3.03M|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (27:7): [True: 10.9k, False: 3.02M]
  ------------------
   28|       |    // We process the data in chunks of 12 bytes.
   29|       |    // Note: using 16 bytes is unsafe, see issue_ossfuzz_71218
   30|  10.9k|    __m128i ascii_first = _mm_cvtepu8_epi16(in);
   31|  10.9k|    __m128i ascii_second = _mm_cvtepu8_epi16(_mm_srli_si128(in, 8));
   32|  10.9k|    if (big_endian) {
  ------------------
  |  Branch (32:9): [Folded, False: 10.9k]
  ------------------
   33|      0|      ascii_first = _mm_shuffle_epi8(ascii_first, swap);
   34|      0|      ascii_second = _mm_shuffle_epi8(ascii_second, swap);
   35|      0|    }
   36|  10.9k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf16_output), ascii_first);
   37|  10.9k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf16_output + 8),
   38|  10.9k|                     ascii_second);
   39|  10.9k|    utf16_output += 12; // We wrote 12 16-bit characters.
   40|  10.9k|    return 12;          // We consumed 12 bytes.
   41|  10.9k|  }
   42|  3.02M|  if (((utf8_end_of_code_point_mask & 0xFFFF) == 0xaaaa)) {
  ------------------
  |  Branch (42:7): [True: 21.1k, False: 3.00M]
  ------------------
   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|  21.1k|    const __m128i sh =
   47|  21.1k|        _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   48|  21.1k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   49|  21.1k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   50|  21.1k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   51|  21.1k|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   52|  21.1k|    if (big_endian)
  ------------------
  |  Branch (52:9): [Folded, False: 21.1k]
  ------------------
   53|      0|      composed = _mm_shuffle_epi8(composed, swap);
   54|  21.1k|    _mm_storeu_si128((__m128i *)utf16_output, composed);
   55|  21.1k|    utf16_output += 8; // We wrote 16 bytes, 8 code points.
   56|  21.1k|    return 16;
   57|  21.1k|  }
   58|  3.00M|  if (input_utf8_end_of_code_point_mask == 0x924) {
  ------------------
  |  Branch (58:7): [True: 2.68k, False: 3.00M]
  ------------------
   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|  2.68k|    const __m128i sh =
   63|  2.68k|        _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
   64|  2.68k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   65|  2.68k|    const __m128i ascii =
   66|  2.68k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
   67|  2.68k|    const __m128i middlebyte =
   68|  2.68k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
   69|  2.68k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
   70|  2.68k|    const __m128i highbyte =
   71|  2.68k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
   72|  2.68k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
   73|  2.68k|    const __m128i composed =
   74|  2.68k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
   75|  2.68k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
   76|  2.68k|    if (big_endian)
  ------------------
  |  Branch (76:9): [Folded, False: 2.68k]
  ------------------
   77|      0|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
   78|  2.68k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
   79|  2.68k|    utf16_output += 4;
   80|  2.68k|    return 12;
   81|  2.68k|  }
   82|       |  /// We do not have a fast path available, so we fallback.
   83|       |
   84|  3.00M|  const uint8_t idx =
   85|  3.00M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
   86|  3.00M|  const uint8_t consumed =
   87|  3.00M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
   88|  3.00M|  if (idx < 64) {
  ------------------
  |  Branch (88:7): [True: 2.57M, False: 427k]
  ------------------
   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.57M|    const __m128i sh =
   96|  2.57M|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
   97|  2.57M|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   98|  2.57M|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   99|  2.57M|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
  100|  2.57M|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
  101|  2.57M|    if (big_endian)
  ------------------
  |  Branch (101:9): [Folded, False: 2.57M]
  ------------------
  102|      0|      composed = _mm_shuffle_epi8(composed, swap);
  103|  2.57M|    _mm_storeu_si128((__m128i *)utf16_output, composed);
  104|  2.57M|    utf16_output += 6; // We wrote 12 bytes, 6 code points.
  105|  2.57M|  } else if (idx < 145) {
  ------------------
  |  Branch (105:14): [True: 346k, False: 81.3k]
  ------------------
  106|       |    // FOUR (4) input code-code units
  107|   346k|    const __m128i sh =
  108|   346k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
  109|   346k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  110|   346k|    const __m128i ascii =
  111|   346k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
  112|   346k|    const __m128i middlebyte =
  113|   346k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
  114|   346k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  115|   346k|    const __m128i highbyte =
  116|   346k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
  117|   346k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
  118|   346k|    const __m128i composed =
  119|   346k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
  120|   346k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
  121|   346k|    if (big_endian)
  ------------------
  |  Branch (121:9): [Folded, False: 346k]
  ------------------
  122|      0|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
  123|   346k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
  124|   346k|    utf16_output += 4;
  125|   346k|  } else if (idx < 209) {
  ------------------
  |  Branch (125:14): [True: 80.5k, False: 808]
  ------------------
  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|  80.5k|    const __m128i sh =
  137|  80.5k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
  138|  80.5k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  139|  80.5k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
  140|  80.5k|    const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
  141|  80.5k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  142|  80.5k|    __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
  143|       |    // correct for spurious high bit
  144|  80.5k|    const __m128i correct =
  145|  80.5k|        _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
  146|  80.5k|    middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
  147|  80.5k|    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|  80.5k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0xff000000));
  151|  80.5k|    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|  80.5k|    const __m128i composed =
  157|  80.5k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
  158|  80.5k|                     _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
  159|  80.5k|    const __m128i composedminus =
  160|  80.5k|        _mm_sub_epi32(composed, _mm_set1_epi32(0x10000));
  161|  80.5k|    const __m128i lowtenbits =
  162|  80.5k|        _mm_and_si128(composedminus, _mm_set1_epi32(0x3ff));
  163|       |    // Notice the 0x3ff mask:
  164|  80.5k|    const __m128i hightenbits =
  165|  80.5k|        _mm_and_si128(_mm_srli_epi32(composedminus, 10), _mm_set1_epi32(0x3ff));
  166|  80.5k|    const __m128i lowtenbitsadd =
  167|  80.5k|        _mm_add_epi32(lowtenbits, _mm_set1_epi32(0xDC00));
  168|  80.5k|    const __m128i hightenbitsadd =
  169|  80.5k|        _mm_add_epi32(hightenbits, _mm_set1_epi32(0xD800));
  170|  80.5k|    const __m128i lowtenbitsaddshifted = _mm_slli_epi32(lowtenbitsadd, 16);
  171|  80.5k|    __m128i surrogates = _mm_or_si128(hightenbitsadd, lowtenbitsaddshifted);
  172|  80.5k|    uint32_t basic_buffer[4];
  173|  80.5k|    uint32_t basic_buffer_swap[4];
  174|  80.5k|    if (big_endian) {
  ------------------
  |  Branch (174:9): [Folded, False: 80.5k]
  ------------------
  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|  80.5k|    _mm_storeu_si128((__m128i *)basic_buffer, composed);
  180|  80.5k|    uint32_t surrogate_buffer[4];
  181|  80.5k|    _mm_storeu_si128((__m128i *)surrogate_buffer, surrogates);
  182|   322k|    for (size_t i = 0; i < 3; i++) {
  ------------------
  |  Branch (182:24): [True: 241k, False: 80.5k]
  ------------------
  183|   241k|      if (basic_buffer[i] > 0x3c00000) {
  ------------------
  |  Branch (183:11): [True: 68.2k, False: 173k]
  ------------------
  184|  68.2k|        utf16_output[0] = uint16_t(surrogate_buffer[i] & 0xffff);
  185|  68.2k|        utf16_output[1] = uint16_t(surrogate_buffer[i] >> 16);
  186|  68.2k|        utf16_output += 2;
  187|   173k|      } else {
  188|   173k|        utf16_output[0] = big_endian ? uint16_t(basic_buffer_swap[i])
  ------------------
  |  Branch (188:27): [Folded, False: 173k]
  ------------------
  189|   173k|                                     : uint16_t(basic_buffer[i]);
  190|   173k|        utf16_output++;
  191|   173k|      }
  192|   241k|    }
  193|  80.5k|  } else {
  194|       |    // here we know that there is an error but we do not handle errors
  195|    808|  }
  196|  3.00M|  return consumed;
  197|  3.00M|}
simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_128convert_masked_utf8_to_utf16ILNS_10endiannessE1EEEmPKcmRPDs:
   10|  3.31M|                                    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|  3.31M|  const __m128i swap =
   23|  3.31M|      _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   24|  3.31M|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   25|  3.31M|  const uint16_t input_utf8_end_of_code_point_mask =
   26|  3.31M|      utf8_end_of_code_point_mask & 0xfff;
   27|  3.31M|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (27:7): [True: 11.3k, False: 3.30M]
  ------------------
   28|       |    // We process the data in chunks of 12 bytes.
   29|       |    // Note: using 16 bytes is unsafe, see issue_ossfuzz_71218
   30|  11.3k|    __m128i ascii_first = _mm_cvtepu8_epi16(in);
   31|  11.3k|    __m128i ascii_second = _mm_cvtepu8_epi16(_mm_srli_si128(in, 8));
   32|  11.3k|    if (big_endian) {
  ------------------
  |  Branch (32:9): [True: 11.3k, Folded]
  ------------------
   33|  11.3k|      ascii_first = _mm_shuffle_epi8(ascii_first, swap);
   34|  11.3k|      ascii_second = _mm_shuffle_epi8(ascii_second, swap);
   35|  11.3k|    }
   36|  11.3k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf16_output), ascii_first);
   37|  11.3k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf16_output + 8),
   38|  11.3k|                     ascii_second);
   39|  11.3k|    utf16_output += 12; // We wrote 12 16-bit characters.
   40|  11.3k|    return 12;          // We consumed 12 bytes.
   41|  11.3k|  }
   42|  3.30M|  if (((utf8_end_of_code_point_mask & 0xFFFF) == 0xaaaa)) {
  ------------------
  |  Branch (42:7): [True: 34.1k, False: 3.26M]
  ------------------
   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|  34.1k|    const __m128i sh =
   47|  34.1k|        _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   48|  34.1k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   49|  34.1k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   50|  34.1k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   51|  34.1k|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   52|  34.1k|    if (big_endian)
  ------------------
  |  Branch (52:9): [True: 34.1k, Folded]
  ------------------
   53|  34.1k|      composed = _mm_shuffle_epi8(composed, swap);
   54|  34.1k|    _mm_storeu_si128((__m128i *)utf16_output, composed);
   55|  34.1k|    utf16_output += 8; // We wrote 16 bytes, 8 code points.
   56|  34.1k|    return 16;
   57|  34.1k|  }
   58|  3.26M|  if (input_utf8_end_of_code_point_mask == 0x924) {
  ------------------
  |  Branch (58:7): [True: 7.02k, False: 3.25M]
  ------------------
   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|  7.02k|    const __m128i sh =
   63|  7.02k|        _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
   64|  7.02k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   65|  7.02k|    const __m128i ascii =
   66|  7.02k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
   67|  7.02k|    const __m128i middlebyte =
   68|  7.02k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
   69|  7.02k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
   70|  7.02k|    const __m128i highbyte =
   71|  7.02k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
   72|  7.02k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
   73|  7.02k|    const __m128i composed =
   74|  7.02k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
   75|  7.02k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
   76|  7.02k|    if (big_endian)
  ------------------
  |  Branch (76:9): [True: 7.02k, Folded]
  ------------------
   77|  7.02k|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
   78|  7.02k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
   79|  7.02k|    utf16_output += 4;
   80|  7.02k|    return 12;
   81|  7.02k|  }
   82|       |  /// We do not have a fast path available, so we fallback.
   83|       |
   84|  3.25M|  const uint8_t idx =
   85|  3.25M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
   86|  3.25M|  const uint8_t consumed =
   87|  3.25M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
   88|  3.25M|  if (idx < 64) {
  ------------------
  |  Branch (88:7): [True: 2.99M, False: 264k]
  ------------------
   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.99M|    const __m128i sh =
   96|  2.99M|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
   97|  2.99M|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   98|  2.99M|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   99|  2.99M|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
  100|  2.99M|    __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
  101|  2.99M|    if (big_endian)
  ------------------
  |  Branch (101:9): [True: 2.99M, Folded]
  ------------------
  102|  2.99M|      composed = _mm_shuffle_epi8(composed, swap);
  103|  2.99M|    _mm_storeu_si128((__m128i *)utf16_output, composed);
  104|  2.99M|    utf16_output += 6; // We wrote 12 bytes, 6 code points.
  105|  2.99M|  } else if (idx < 145) {
  ------------------
  |  Branch (105:14): [True: 175k, False: 89.2k]
  ------------------
  106|       |    // FOUR (4) input code-code units
  107|   175k|    const __m128i sh =
  108|   175k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
  109|   175k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  110|   175k|    const __m128i ascii =
  111|   175k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
  112|   175k|    const __m128i middlebyte =
  113|   175k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
  114|   175k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  115|   175k|    const __m128i highbyte =
  116|   175k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
  117|   175k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
  118|   175k|    const __m128i composed =
  119|   175k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
  120|   175k|    __m128i composed_repacked = _mm_packus_epi32(composed, composed);
  121|   175k|    if (big_endian)
  ------------------
  |  Branch (121:9): [True: 175k, Folded]
  ------------------
  122|   175k|      composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
  123|   175k|    _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
  124|   175k|    utf16_output += 4;
  125|   175k|  } else if (idx < 209) {
  ------------------
  |  Branch (125:14): [True: 88.2k, False: 1.02k]
  ------------------
  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|  88.2k|    const __m128i sh =
  137|  88.2k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
  138|  88.2k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  139|  88.2k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
  140|  88.2k|    const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
  141|  88.2k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  142|  88.2k|    __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
  143|       |    // correct for spurious high bit
  144|  88.2k|    const __m128i correct =
  145|  88.2k|        _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
  146|  88.2k|    middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
  147|  88.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|  88.2k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0xff000000));
  151|  88.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|  88.2k|    const __m128i composed =
  157|  88.2k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
  158|  88.2k|                     _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
  159|  88.2k|    const __m128i composedminus =
  160|  88.2k|        _mm_sub_epi32(composed, _mm_set1_epi32(0x10000));
  161|  88.2k|    const __m128i lowtenbits =
  162|  88.2k|        _mm_and_si128(composedminus, _mm_set1_epi32(0x3ff));
  163|       |    // Notice the 0x3ff mask:
  164|  88.2k|    const __m128i hightenbits =
  165|  88.2k|        _mm_and_si128(_mm_srli_epi32(composedminus, 10), _mm_set1_epi32(0x3ff));
  166|  88.2k|    const __m128i lowtenbitsadd =
  167|  88.2k|        _mm_add_epi32(lowtenbits, _mm_set1_epi32(0xDC00));
  168|  88.2k|    const __m128i hightenbitsadd =
  169|  88.2k|        _mm_add_epi32(hightenbits, _mm_set1_epi32(0xD800));
  170|  88.2k|    const __m128i lowtenbitsaddshifted = _mm_slli_epi32(lowtenbitsadd, 16);
  171|  88.2k|    __m128i surrogates = _mm_or_si128(hightenbitsadd, lowtenbitsaddshifted);
  172|  88.2k|    uint32_t basic_buffer[4];
  173|  88.2k|    uint32_t basic_buffer_swap[4];
  174|  88.2k|    if (big_endian) {
  ------------------
  |  Branch (174:9): [True: 88.2k, Folded]
  ------------------
  175|  88.2k|      _mm_storeu_si128((__m128i *)basic_buffer_swap,
  176|  88.2k|                       _mm_shuffle_epi8(composed, swap));
  177|  88.2k|      surrogates = _mm_shuffle_epi8(surrogates, swap);
  178|  88.2k|    }
  179|  88.2k|    _mm_storeu_si128((__m128i *)basic_buffer, composed);
  180|  88.2k|    uint32_t surrogate_buffer[4];
  181|  88.2k|    _mm_storeu_si128((__m128i *)surrogate_buffer, surrogates);
  182|   353k|    for (size_t i = 0; i < 3; i++) {
  ------------------
  |  Branch (182:24): [True: 264k, False: 88.2k]
  ------------------
  183|   264k|      if (basic_buffer[i] > 0x3c00000) {
  ------------------
  |  Branch (183:11): [True: 74.4k, False: 190k]
  ------------------
  184|  74.4k|        utf16_output[0] = uint16_t(surrogate_buffer[i] & 0xffff);
  185|  74.4k|        utf16_output[1] = uint16_t(surrogate_buffer[i] >> 16);
  186|  74.4k|        utf16_output += 2;
  187|   190k|      } else {
  188|   190k|        utf16_output[0] = big_endian ? uint16_t(basic_buffer_swap[i])
  ------------------
  |  Branch (188:27): [True: 190k, Folded]
  ------------------
  189|   190k|                                     : uint16_t(basic_buffer[i]);
  190|   190k|        utf16_output++;
  191|   190k|      }
  192|   264k|    }
  193|  88.2k|  } else {
  194|       |    // here we know that there is an error but we do not handle errors
  195|  1.02k|  }
  196|  3.25M|  return consumed;
  197|  3.26M|}

simdutf.cpp:_ZN7simdutf8westmere12_GLOBAL__N_128convert_masked_utf8_to_utf32EPKcmRPDi:
    9|  3.61M|                                    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.61M|  const __m128i in = _mm_loadu_si128((__m128i *)input);
   22|  3.61M|  const uint16_t input_utf8_end_of_code_point_mask =
   23|  3.61M|      utf8_end_of_code_point_mask & 0xfff;
   24|  3.61M|  if (utf8_end_of_code_point_mask == 0xfff) {
  ------------------
  |  Branch (24:7): [True: 10.0k, False: 3.60M]
  ------------------
   25|       |    // We process the data in chunks of 12 bytes.
   26|  10.0k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output),
   27|  10.0k|                     _mm_cvtepu8_epi32(in));
   28|  10.0k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 4),
   29|  10.0k|                     _mm_cvtepu8_epi32(_mm_srli_si128(in, 4)));
   30|  10.0k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 8),
   31|  10.0k|                     _mm_cvtepu8_epi32(_mm_srli_si128(in, 8)));
   32|  10.0k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 12),
   33|  10.0k|                     _mm_cvtepu8_epi32(_mm_srli_si128(in, 12)));
   34|  10.0k|    utf32_output += 12; // We wrote 12 32-bit characters.
   35|  10.0k|    return 12;          // We consumed 12 bytes.
   36|  10.0k|  }
   37|  3.60M|  if (((utf8_end_of_code_point_mask & 0xffff) == 0xaaaa)) {
  ------------------
  |  Branch (37:7): [True: 24.8k, False: 3.58M]
  ------------------
   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|  24.8k|    const __m128i sh =
   42|  24.8k|        _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
   43|  24.8k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   44|  24.8k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   45|  24.8k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   46|  24.8k|    const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   47|  24.8k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output),
   48|  24.8k|                     _mm_cvtepu16_epi32(composed));
   49|  24.8k|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 4),
   50|  24.8k|                     _mm_cvtepu16_epi32(_mm_srli_si128(composed, 8)));
   51|  24.8k|    utf32_output += 8; // We wrote 32 bytes, 8 code points.
   52|  24.8k|    return 16;
   53|  24.8k|  }
   54|  3.58M|  if (input_utf8_end_of_code_point_mask == 0x924) {
  ------------------
  |  Branch (54:7): [True: 4.36k, False: 3.57M]
  ------------------
   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|  4.36k|    const __m128i sh =
   59|  4.36k|        _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
   60|  4.36k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   61|  4.36k|    const __m128i ascii =
   62|  4.36k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
   63|  4.36k|    const __m128i middlebyte =
   64|  4.36k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
   65|  4.36k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
   66|  4.36k|    const __m128i highbyte =
   67|  4.36k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
   68|  4.36k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
   69|  4.36k|    const __m128i composed =
   70|  4.36k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
   71|  4.36k|    _mm_storeu_si128((__m128i *)utf32_output, composed);
   72|  4.36k|    utf32_output += 4;
   73|  4.36k|    return 12;
   74|  4.36k|  }
   75|       |  /// We do not have a fast path available, so we fallback.
   76|       |
   77|  3.57M|  const uint8_t idx =
   78|  3.57M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
   79|  3.57M|  const uint8_t consumed =
   80|  3.57M|      tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
   81|  3.57M|  if (idx < 64) {
  ------------------
  |  Branch (81:7): [True: 3.29M, False: 278k]
  ------------------
   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.29M|    const __m128i sh =
   89|  3.29M|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
   90|  3.29M|    const __m128i perm = _mm_shuffle_epi8(in, sh);
   91|  3.29M|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
   92|  3.29M|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
   93|  3.29M|    const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
   94|  3.29M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output),
   95|  3.29M|                     _mm_cvtepu16_epi32(composed));
   96|  3.29M|    _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output + 4),
   97|  3.29M|                     _mm_cvtepu16_epi32(_mm_srli_si128(composed, 8)));
   98|  3.29M|    utf32_output += 6; // We wrote 12 bytes, 6 code points.
   99|  3.29M|  } else if (idx < 145) {
  ------------------
  |  Branch (99:14): [True: 197k, False: 81.0k]
  ------------------
  100|       |    // FOUR (4) input code-code units
  101|   197k|    const __m128i sh =
  102|   197k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
  103|   197k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  104|   197k|    const __m128i ascii =
  105|   197k|        _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
  106|   197k|    const __m128i middlebyte =
  107|   197k|        _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
  108|   197k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  109|   197k|    const __m128i highbyte =
  110|   197k|        _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
  111|   197k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
  112|   197k|    const __m128i composed =
  113|   197k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
  114|   197k|    _mm_storeu_si128((__m128i *)utf32_output, composed);
  115|   197k|    utf32_output += 4;
  116|   197k|  } else if (idx < 209) {
  ------------------
  |  Branch (116:14): [True: 80.1k, False: 930]
  ------------------
  117|       |    // TWO (2) input code-code units
  118|  80.1k|    const __m128i sh =
  119|  80.1k|        _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
  120|  80.1k|    const __m128i perm = _mm_shuffle_epi8(in, sh);
  121|  80.1k|    const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
  122|  80.1k|    const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
  123|  80.1k|    const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
  124|  80.1k|    __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
  125|       |    // correct for spurious high bit
  126|  80.1k|    const __m128i correct =
  127|  80.1k|        _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
  128|  80.1k|    middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
  129|  80.1k|    const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4);
  130|  80.1k|    const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0x07000000));
  131|  80.1k|    const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6);
  132|  80.1k|    const __m128i composed =
  133|  80.1k|        _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
  134|  80.1k|                     _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
  135|  80.1k|    _mm_storeu_si128((__m128i *)utf32_output, composed);
  136|  80.1k|    utf32_output += 3;
  137|  80.1k|  } else {
  138|       |    // here we know that there is an error but we do not handle errors
  139|    930|  }
  140|  3.57M|  return consumed;
  141|  3.58M|}

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

