From 1b54b3be7c1cad240783830379f3da0dcc873381 Mon Sep 17 00:00:00 2001 From: Dan Kahyan Date: Thu, 23 Jul 2026 14:32:40 +0300 Subject: [PATCH 1/2] Add RISC-V Vector Extension config macro The BOOST_UUID_USE_RISCV_V config macro is automatically defined when the compiler defines __riscv_v, indicating RISC-V Vector Extension (V 1.0+) support is available. The macro is also added to the BOOST_UUID_NO_SIMD exclusion list. --- include/boost/uuid/detail/config.hpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/include/boost/uuid/detail/config.hpp b/include/boost/uuid/detail/config.hpp index 0d886eff..29752e10 100644 --- a/include/boost/uuid/detail/config.hpp +++ b/include/boost/uuid/detail/config.hpp @@ -29,6 +29,7 @@ #undef BOOST_UUID_USE_AVX2 #undef BOOST_UUID_USE_AVX512_V1 #undef BOOST_UUID_USE_AVX10_1 +#undef BOOST_UUID_USE_RISCV_V #else @@ -91,6 +92,15 @@ #endif +#if defined(__riscv_v) + +// RISC-V Vector Extension (V 1.0+), GCC 12+ / Clang 15+ +#ifndef BOOST_UUID_USE_RISCV_V +#define BOOST_UUID_USE_RISCV_V +#endif + +#endif + // More advanced ISA extensions imply less advanced are also available #if !defined(BOOST_UUID_USE_AVX512_V1) && defined(BOOST_UUID_USE_AVX10_1) #define BOOST_UUID_USE_AVX512_V1 @@ -128,7 +138,8 @@ !defined(BOOST_UUID_USE_SSE41) && \ !defined(BOOST_UUID_USE_SSSE3) && \ !defined(BOOST_UUID_USE_SSE3) && \ - !defined(BOOST_UUID_USE_SSE2) + !defined(BOOST_UUID_USE_SSE2) && \ + !defined(BOOST_UUID_USE_RISCV_V) #define BOOST_UUID_NO_SIMD #endif From a3b8f19c17861507a13cbbfa4b9b396f82699694 Mon Sep 17 00:00:00 2001 From: Dan Kahyan Date: Thu, 23 Jul 2026 14:32:51 +0300 Subject: [PATCH 2/2] Add RISC-V Vector implementation of from_chars This adds a RISC-V Vector (RVV) implementation of the from_chars algorithm, ported from the x86 SIMD implementation. The RVV version supports VLEN >= 128 and handles narrow and wide character types (char, char16_t, char32_t). The implementation follows the same structure as from_chars_x86.hpp: deinterleaving of input character pairs, dash validation, hex character validation, and nibble packing. It uses two separate expected dash vectors (one per character pair) instead of x86's single shuffled vector, as RVV's vrgather-based deinterleaving makes this approach more natural. The generic fallback path is preserved: both the x86 and RISC-V SIMD paths dispatch to from_chars_generic when std::is_constant_evaluated() is true. --- include/boost/uuid/detail/from_chars.hpp | 5 +- .../boost/uuid/detail/from_chars_riscv.hpp | 735 ++++++++++++++++++ 2 files changed, 739 insertions(+), 1 deletion(-) create mode 100644 include/boost/uuid/detail/from_chars_riscv.hpp diff --git a/include/boost/uuid/detail/from_chars.hpp b/include/boost/uuid/detail/from_chars.hpp index 2639a724..572b598a 100644 --- a/include/boost/uuid/detail/from_chars.hpp +++ b/include/boost/uuid/detail/from_chars.hpp @@ -14,6 +14,9 @@ #if defined(BOOST_UUID_USE_SSE2) # include +#elif defined(BOOST_UUID_USE_RISCV_V) +# include + #elif defined(BOOST_UUID_REPORT_IMPLEMENTATION) # include BOOST_PRAGMA_MESSAGE( "Using from_chars_generic.hpp" ) @@ -27,7 +30,7 @@ template BOOST_UUID_CXX14_CONSTEXPR_RT inline from_chars_result from_chars( Ch const* first, Ch const* last, uuid& u ) noexcept { -#if defined(BOOST_UUID_USE_SSE2) +#if defined(BOOST_UUID_USE_SSE2) || defined(BOOST_UUID_USE_RISCV_V) if( detail::is_constant_evaluated_rt() ) { return detail::from_chars_generic( first, last, u ); diff --git a/include/boost/uuid/detail/from_chars_riscv.hpp b/include/boost/uuid/detail/from_chars_riscv.hpp new file mode 100644 index 00000000..39353058 --- /dev/null +++ b/include/boost/uuid/detail/from_chars_riscv.hpp @@ -0,0 +1,735 @@ +#ifndef BOOST_UUID_DETAIL_FROM_CHARS_RISCV_HPP_INCLUDED +#define BOOST_UUID_DETAIL_FROM_CHARS_RISCV_HPP_INCLUDED + +// Copyright 2025 Andrey Semashev +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#if defined(BOOST_UUID_USE_RISCV_V) + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_UUID_REPORT_IMPLEMENTATION) +#include +BOOST_PRAGMA_MESSAGE( "Using from_chars_riscv.hpp, RISC-V Vector Extension" ) +#endif // #if defined(BOOST_UUID_REPORT_IMPLEMENTATION) + +namespace boost { +namespace uuids { +namespace detail { + +template< + typename Char, + bool IsCharASCIICompatible = ('0' == 0x30 && '9' == 0x39 && 'A' == 0x41 && 'F' == 0x46 && 'a' == 0x61 && 'f' == 0x66 && '-' == 0x2D), + bool IsWCharASCIICompatible = (L'0' == 0x30 && L'9' == 0x39 && L'A' == 0x41 && L'F' == 0x46 && L'a' == 0x61 && L'f' == 0x66 && L'-' == 0x2D) +> +struct from_chars_simd_char_constants +{ + static const simd_vector128< std::uint8_t > mm_expected_dashes1; + static const simd_vector128< std::uint8_t > mm_expected_dashes2; + + static constexpr std::uint8_t char_code2 = 0x61; // 'a' in ASCII + static constexpr std::uint8_t char_code2_sub = static_cast< std::uint8_t >(char_code2 - 10u); + static constexpr std::uint8_t char_code1 = 0x41; // 'A' in ASCII + static constexpr std::uint8_t char_code1_sub = static_cast< std::uint8_t >(char_code1 - 10u); + static constexpr std::uint8_t char_code0 = 0x30; // '0' in ASCII + static constexpr std::uint8_t char_code0_sub = char_code0; + + static constexpr std::uint32_t char_code_sub = + (static_cast< std::uint32_t >(char_code0_sub) << 16u) | (static_cast< std::uint32_t >(char_code1_sub) << 8u) | char_code2_sub; + + static const simd_vector128< std::uint8_t > mm_char_code2_cmp; + static const simd_vector128< std::uint8_t > mm_char_code1_cmp; + + static const simd_vector128< std::uint8_t > mm_char_code2_sub; + static const simd_vector128< std::uint8_t > mm_char_code1_sub; + static const simd_vector128< std::uint8_t > mm_char_code0_sub; +}; + +template< typename Char, bool IsCharASCIICompatible, bool IsWCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< Char, IsCharASCIICompatible, IsWCharASCIICompatible >::mm_expected_dashes1 = + {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00 }}; + +template< typename Char, bool IsCharASCIICompatible, bool IsWCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< Char, IsCharASCIICompatible, IsWCharASCIICompatible >::mm_expected_dashes2 = + {{ 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}; + +template< typename Char, bool IsCharASCIICompatible, bool IsWCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< Char, IsCharASCIICompatible, IsWCharASCIICompatible >::mm_char_code2_cmp = +{{ + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u) +}}; + +template< typename Char, bool IsCharASCIICompatible, bool IsWCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< Char, IsCharASCIICompatible, IsWCharASCIICompatible >::mm_char_code1_cmp = +{{ + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u) +}}; + +template< typename Char, bool IsCharASCIICompatible, bool IsWCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< Char, IsCharASCIICompatible, IsWCharASCIICompatible >::mm_char_code2_sub = +{{ + char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, + char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub +}}; + +template< typename Char, bool IsCharASCIICompatible, bool IsWCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< Char, IsCharASCIICompatible, IsWCharASCIICompatible >::mm_char_code1_sub = +{{ + char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, + char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub +}}; + +template< typename Char, bool IsCharASCIICompatible, bool IsWCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< Char, IsCharASCIICompatible, IsWCharASCIICompatible >::mm_char_code0_sub = +{{ + char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, + char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub +}}; + +template< bool IsWCharASCIICompatible > +struct from_chars_simd_char_constants< char, false, IsWCharASCIICompatible > +{ + static_assert(static_cast< std::int8_t >('0') > -128 && static_cast< std::int8_t >('A') > -128 && static_cast< std::int8_t >('a') > -128, + "Boost.UUID: Unsupported char encoding, hexadecimal character codes are expected to be greater than -128"); + + static const simd_vector128< std::uint8_t > mm_expected_dashes1; + static const simd_vector128< std::uint8_t > mm_expected_dashes2; + + static constexpr std::uint8_t char_code2 = static_cast< std::uint8_t > + ( + static_cast< std::int8_t >('a') > static_cast< std::int8_t >('A') ? + ( + static_cast< std::int8_t >('a') > static_cast< std::int8_t >('0') ? 'a' : '0' + ) : + ( + static_cast< std::int8_t >('A') > static_cast< std::int8_t >('0') ? 'A' : '0' + ) + ); + static constexpr std::uint8_t char_code2_sub = char_code2 == static_cast< std::uint8_t >('0') ? + static_cast< std::uint8_t >('0') : static_cast< std::uint8_t >(char_code2 - 10u); + + static constexpr std::uint8_t char_code1 = static_cast< std::uint8_t > + ( + static_cast< std::int8_t >('a') > static_cast< std::int8_t >('A') ? + ( + static_cast< std::int8_t >('a') < static_cast< std::int8_t >('0') ? 'a' : '0' + ) : + ( + static_cast< std::int8_t >('A') < static_cast< std::int8_t >('0') ? 'A' : '0' + ) + ); + static constexpr std::uint8_t char_code1_sub = char_code1 == static_cast< std::uint8_t >('0') ? + static_cast< std::uint8_t >('0') : static_cast< std::uint8_t >(char_code1 - 10u); + + static constexpr std::uint8_t char_code0 = static_cast< std::uint8_t > + ( + static_cast< std::int8_t >('a') < static_cast< std::int8_t >('A') ? + ( + static_cast< std::int8_t >('a') < static_cast< std::int8_t >('0') ? 'a' : '0' + ) : + ( + static_cast< std::int8_t >('A') < static_cast< std::int8_t >('0') ? 'A' : '0' + ) + ); + static constexpr std::uint8_t char_code0_sub = char_code0 == static_cast< std::uint8_t >('0') ? + static_cast< std::uint8_t >('0') : static_cast< std::uint8_t >(char_code0 - 10u); + + static constexpr std::uint32_t char_code_sub = + (static_cast< std::uint32_t >(char_code0_sub) << 16u) | (static_cast< std::uint32_t >(char_code1_sub) << 8u) | char_code2_sub; + + static const simd_vector128< std::uint8_t > mm_char_code2_cmp; + static const simd_vector128< std::uint8_t > mm_char_code1_cmp; + + static const simd_vector128< std::uint8_t > mm_char_code2_sub; + static const simd_vector128< std::uint8_t > mm_char_code1_sub; + static const simd_vector128< std::uint8_t > mm_char_code0_sub; +}; + +template< bool IsWCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< char, false, IsWCharASCIICompatible >::mm_expected_dashes1 = +{{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + static_cast< std::uint8_t >('-'), 0x00, 0x00, 0x00, 0x00, static_cast< std::uint8_t >('-'), 0x00, 0x00 +}}; + +template< bool IsWCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< char, false, IsWCharASCIICompatible >::mm_expected_dashes2 = +{{ + 0x00, 0x00, static_cast< std::uint8_t >('-'), 0x00, 0x00, 0x00, 0x00, static_cast< std::uint8_t >('-'), + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}}; + +template< bool IsWCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< char, false, IsWCharASCIICompatible >::mm_char_code2_cmp = +{{ + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u) +}}; + +template< bool IsWCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< char, false, IsWCharASCIICompatible >::mm_char_code1_cmp = +{{ + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u) +}}; + +template< bool IsWCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< char, false, IsWCharASCIICompatible >::mm_char_code2_sub = +{{ + char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, + char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub +}}; + +template< bool IsWCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< char, false, IsWCharASCIICompatible >::mm_char_code1_sub = +{{ + char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, + char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub +}}; + +template< bool IsWCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< char, false, IsWCharASCIICompatible >::mm_char_code0_sub = +{{ + char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, + char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub +}}; + +template< bool IsCharASCIICompatible > +struct from_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false > +{ + static_assert(static_cast< wchar_t >(static_cast< std::uint8_t >(L'0')) == L'0' && static_cast< wchar_t >(static_cast< std::uint8_t >(L'9')) == L'9' && + static_cast< wchar_t >(static_cast< std::uint8_t >(L'a')) == L'a' && static_cast< wchar_t >(static_cast< std::uint8_t >(L'f')) == L'f' && + static_cast< wchar_t >(static_cast< std::uint8_t >(L'-')) == L'-', + "Boost.UUID: Unsupported wchar_t encoding, hexadecimal and dash character codes are expected to be representable by a single byte"); + + static_assert(static_cast< std::int8_t >(L'0') > -128 && static_cast< std::int8_t >(L'A') > -128 && static_cast< std::int8_t >(L'a') > -128, + "Boost.UUID: Unsupported wchar_t encoding, hexadecimal character codes are expected to be greater than -128"); + + static const simd_vector128< std::uint8_t > mm_expected_dashes1; + static const simd_vector128< std::uint8_t > mm_expected_dashes2; + + static constexpr std::uint8_t char_code2 = static_cast< std::uint8_t > + ( + static_cast< std::int8_t >(L'a') > static_cast< std::int8_t >(L'A') ? + ( + static_cast< std::int8_t >(L'a') > static_cast< std::int8_t >(L'0') ? L'a' : L'0' + ) : + ( + static_cast< std::int8_t >(L'A') > static_cast< std::int8_t >(L'0') ? L'A' : L'0' + ) + ); + static constexpr std::uint8_t char_code2_sub = char_code2 == static_cast< std::uint8_t >(L'0') ? + static_cast< std::uint8_t >(L'0') : static_cast< std::uint8_t >(char_code2 - 10u); + + static constexpr std::uint8_t char_code1 = static_cast< std::uint8_t > + ( + static_cast< std::int8_t >(L'a') > static_cast< std::int8_t >(L'A') ? + ( + static_cast< std::int8_t >(L'a') < static_cast< std::int8_t >(L'0') ? L'a' : L'0' + ) : + ( + static_cast< std::int8_t >(L'A') < static_cast< std::int8_t >(L'0') ? L'A' : L'0' + ) + ); + static constexpr std::uint8_t char_code1_sub = char_code1 == static_cast< std::uint8_t >(L'0') ? + static_cast< std::uint8_t >(L'0') : static_cast< std::uint8_t >(char_code1 - 10u); + + static constexpr std::uint8_t char_code0 = static_cast< std::uint8_t > + ( + static_cast< std::int8_t >(L'a') < static_cast< std::int8_t >(L'A') ? + ( + static_cast< std::int8_t >(L'a') < static_cast< std::int8_t >(L'0') ? L'a' : L'0' + ) : + ( + static_cast< std::int8_t >(L'A') < static_cast< std::int8_t >(L'0') ? L'A' : L'0' + ) + ); + static constexpr std::uint8_t char_code0_sub = char_code0 == static_cast< std::uint8_t >(L'0') ? + static_cast< std::uint8_t >(L'0') : static_cast< std::uint8_t >(char_code0 - 10u); + + static constexpr std::uint32_t char_code_sub = + (static_cast< std::uint32_t >(char_code0_sub) << 16u) | (static_cast< std::uint32_t >(char_code1_sub) << 8u) | char_code2_sub; + + static const simd_vector128< std::uint8_t > mm_char_code2_cmp; + static const simd_vector128< std::uint8_t > mm_char_code1_cmp; + + static const simd_vector128< std::uint8_t > mm_char_code2_sub; + static const simd_vector128< std::uint8_t > mm_char_code1_sub; + static const simd_vector128< std::uint8_t > mm_char_code0_sub; +}; + +template< bool IsCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false >::mm_expected_dashes1 = +{{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + static_cast< std::uint8_t >(L'-'), 0x00, 0x00, 0x00, 0x00, static_cast< std::uint8_t >(L'-'), 0x00, 0x00 +}}; + +template< bool IsCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false >::mm_expected_dashes2 = +{{ + 0x00, 0x00, static_cast< std::uint8_t >(L'-'), 0x00, 0x00, 0x00, 0x00, static_cast< std::uint8_t >(L'-'), + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}}; + +template< bool IsCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false >::mm_char_code2_cmp = +{{ + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u), + static_cast< std::uint8_t >(char_code2 - 1u), static_cast< std::uint8_t >(char_code2 - 1u) +}}; + +template< bool IsCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false >::mm_char_code1_cmp = +{{ + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u), + static_cast< std::uint8_t >(char_code1 - 1u), static_cast< std::uint8_t >(char_code1 - 1u) +}}; + +template< bool IsCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false >::mm_char_code2_sub = +{{ + char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, + char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub, char_code2_sub +}}; + +template< bool IsCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false >::mm_char_code1_sub = +{{ + char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, + char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub, char_code1_sub +}}; + +template< bool IsCharASCIICompatible > +const simd_vector128< std::uint8_t > from_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false >::mm_char_code0_sub = +{{ + char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, + char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub, char_code0_sub +}}; + + +template< typename > +struct from_chars_simd_constants +{ + static const simd_vector128< std::uint8_t > mm_F0; + + // Gather patterns for deinterleaving even and odd positioned hex digits from the three input vectors. + // Each index is a position within the source vector (chars1, chars2 or chars3). + static const simd_vector128< std::uint8_t > mm_upper_idx_chars1; + static const simd_vector128< std::uint8_t > mm_upper_idx_chars2; + static const simd_vector128< std::uint8_t > mm_upper_idx_chars3; + + static const simd_vector128< std::uint8_t > mm_lower_idx_chars1; + static const simd_vector128< std::uint8_t > mm_lower_idx_chars2; + static const simd_vector128< std::uint8_t > mm_lower_idx_chars3; +}; + +template< typename T > +const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_F0 = + {{ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0 }}; + +// Even-positioned hex digits in the original 36-character string become the high nibbles of the output bytes. +template< typename T > +const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_upper_idx_chars1 = + {{ 0x00, 0x02, 0x04, 0x06, 0x09, 0x0B, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}; + +template< typename T > +const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_upper_idx_chars2 = + {{ 0x00, 0x03, 0x05, 0x08, 0x0A, 0x0C, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}; + +template< typename T > +const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_upper_idx_chars3 = + {{ 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}; + +// Odd-positioned hex digits in the original 36-character string become the low nibbles of the output bytes. +template< typename T > +const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_lower_idx_chars1 = + {{ 0x01, 0x03, 0x05, 0x07, 0x0A, 0x0C, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}; + +template< typename T > +const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_lower_idx_chars2 = + {{ 0x01, 0x04, 0x06, 0x09, 0x0B, 0x0D, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}; + +template< typename T > +const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_lower_idx_chars3 = + {{ 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}; + + +BOOST_FORCEINLINE vuint8m1_t load_vector128(const simd_vector128< std::uint8_t >& v, size_t vl) noexcept +{ + return __riscv_vle8_v_u8m1(v.bytes, vl); +} + +template< typename Char, std::size_t Size = sizeof(Char) > +struct from_chars_simd_load_traits; + +template< typename Char > +struct from_chars_simd_load_traits< Char, 1u > +{ + static BOOST_FORCEINLINE vuint8m1_t load_packed_16(const Char* p, size_t vl) noexcept + { + return __riscv_vle8_v_u8m1(reinterpret_cast< const std::uint8_t* >(p), vl); + } + + static BOOST_FORCEINLINE vuint8m1_t load_packed_4(const Char* p, size_t vl) noexcept + { + (void)vl; + return __riscv_vle8_v_u8m1(reinterpret_cast< const std::uint8_t* >(p), 4); + } + + static BOOST_FORCEINLINE vuint8m1_t load_packed_n(const Char* p, unsigned int n, size_t vl) noexcept + { + alignas(16) std::uint8_t buf[16] = {}; + detail::memcpy(buf, p, n); + return __riscv_vle8_v_u8m1(buf, vl); + } +}; + +template< typename Char > +struct from_chars_simd_load_traits< Char, 2u > +{ + static BOOST_FORCEINLINE vuint8m1_t load_packed_16(const Char* p, size_t vl) noexcept + { + (void)vl; + size_t vl16 = __riscv_vsetvl_e16m2(16); + vuint16m2_t v16 = __riscv_vle16_v_u16m2(reinterpret_cast< const std::uint16_t* >(p), vl16); + return __riscv_vnsrl_wx_u8m1(v16, 0, 16); + } + + static BOOST_FORCEINLINE vuint8m1_t load_packed_4(const Char* p, size_t vl) noexcept + { + (void)vl; + vuint16m1_t v16 = __riscv_vle16_v_u16m1(reinterpret_cast< const std::uint16_t* >(p), 4); + vuint16m2_t v16_w = __riscv_vlmul_ext_v_u16m1_u16m2(v16); + return __riscv_vnsrl_wx_u8m1(v16_w, 0, 4); + } + + static BOOST_FORCEINLINE vuint8m1_t load_packed_n(const Char* p, unsigned int n, size_t vl) noexcept + { + alignas(16) std::uint8_t buf[16] = {}; + for (unsigned int i = 0u; i < n; ++i) + { + buf[i] = static_cast< std::uint8_t >(p[i]); + } + return __riscv_vle8_v_u8m1(buf, vl); + } +}; + +template< typename Char > +struct from_chars_simd_load_traits< Char, 4u > +{ + static BOOST_FORCEINLINE vuint8m1_t load_packed_16(const Char* p, size_t vl) noexcept + { + (void)vl; + size_t vl32 = __riscv_vsetvl_e32m4(16); + vuint32m4_t v32 = __riscv_vle32_v_u32m4(reinterpret_cast< const std::uint32_t* >(p), vl32); + vuint16m2_t v16 = __riscv_vnsrl_wx_u16m2(v32, 0, 16); + return __riscv_vnsrl_wx_u8m1(v16, 0, 16); + } + + static BOOST_FORCEINLINE vuint8m1_t load_packed_4(const Char* p, size_t vl) noexcept + { + (void)vl; + vuint32m1_t v32 = __riscv_vle32_v_u32m1(reinterpret_cast< const std::uint32_t* >(p), 4); + vuint32m4_t v32_w = __riscv_vlmul_ext_v_u32m1_u32m4(v32); + vuint16m2_t v16 = __riscv_vnsrl_wx_u16m2(v32_w, 0, 4); + return __riscv_vnsrl_wx_u8m1(v16, 0, 4); + } + + static BOOST_FORCEINLINE vuint8m1_t load_packed_n(const Char* p, unsigned int n, size_t vl) noexcept + { + alignas(16) std::uint8_t buf[16] = {}; + for (unsigned int i = 0u; i < n; ++i) + { + buf[i] = static_cast< std::uint8_t >(p[i]); + } + return __riscv_vle8_v_u8m1(buf, vl); + } +}; + + +/*! + * Converts a string of 36 hexadecimal UUID characters in `chars1`/`chars2`/`chars3` to a 16-byte binary value and, + * if successful, stores it into `data`. If not successful, stores the failure character position to `end_pos` + * and error code to `ec`. + */ +BOOST_FORCEINLINE void from_chars_simd_core +( + vuint8m1_t chars1, vuint8m1_t chars2, vuint8m1_t chars3, + vuint8m1_t const& expected_dashes1, + vuint8m1_t const& expected_dashes2, + vuint8m1_t const& char_code1_cmp, + vuint8m1_t const& char_code2_cmp, + vuint8m1_t const& char_code0_sub, + vuint8m1_t const& char_code1_sub, + vuint8m1_t const& char_code2_sub, + std::uint8_t* data, unsigned int& end_pos, from_chars_error& ec, size_t vl +) +{ + using constants = uuids::detail::from_chars_simd_constants< void >; + + // Check if dashes are in the expected positions + { + const vuint8m1_t dash_char = __riscv_vmv_v_x_u8m1(static_cast< std::uint8_t >('-'), vl); + + const vbool8_t expected1 = __riscv_vmsgtu_vx_u8m1_b8(expected_dashes1, 0, vl); + const vbool8_t expected2 = __riscv_vmsgtu_vx_u8m1_b8(expected_dashes2, 0, vl); + + const vbool8_t is_dash1 = __riscv_vmseq_vv_u8m1_b8(chars1, dash_char, vl); + const vbool8_t is_dash2 = __riscv_vmseq_vv_u8m1_b8(chars2, dash_char, vl); + + const vbool8_t missing1 = __riscv_vmandn_mm_b8(expected1, is_dash1, vl); + const vbool8_t missing2 = __riscv_vmandn_mm_b8(expected2, is_dash2, vl); + + const long first_missing1 = __riscv_vfirst_m_b8(missing1, vl); + const long first_missing2 = __riscv_vfirst_m_b8(missing2, vl); + + unsigned int dash_pos = 0xFFFFFFFFu; + if (first_missing1 >= 0) + { + dash_pos = static_cast< unsigned int >(first_missing1); + } + if (first_missing2 >= 0) + { + const unsigned int pos2 = static_cast< unsigned int >(first_missing2) + 16u; + if (pos2 < dash_pos) + dash_pos = pos2; + } + + if (BOOST_UNLIKELY(dash_pos < end_pos)) + { + end_pos = dash_pos; + ec = from_chars_error::dash_expected; + } + } + + // Deinterleave even and odd positioned hex digits into upper and lower nibbles + vuint8m1_t upper = __riscv_vmv_v_x_u8m1(0, vl); + vuint8m1_t lower = __riscv_vmv_v_x_u8m1(0, vl); + + { + const vuint8m1_t upper_idx1 = load_vector128(constants::mm_upper_idx_chars1, vl); + const vuint8m1_t upper_idx2 = load_vector128(constants::mm_upper_idx_chars2, vl); + const vuint8m1_t upper_idx3 = load_vector128(constants::mm_upper_idx_chars3, vl); + + const vuint8m1_t upper1 = __riscv_vrgather_vv_u8m1(chars1, upper_idx1, vl); + const vuint8m1_t upper2 = __riscv_vrgather_vv_u8m1(chars2, upper_idx2, vl); + const vuint8m1_t upper3 = __riscv_vrgather_vv_u8m1(chars3, upper_idx3, vl); + + upper = __riscv_vslideup_vx_u8m1(upper, upper1, 0, vl); + upper = __riscv_vslideup_vx_u8m1(upper, upper2, 7, vl); + upper = __riscv_vslideup_vx_u8m1(upper, upper3, 14, vl); + } + + { + const vuint8m1_t lower_idx1 = load_vector128(constants::mm_lower_idx_chars1, vl); + const vuint8m1_t lower_idx2 = load_vector128(constants::mm_lower_idx_chars2, vl); + const vuint8m1_t lower_idx3 = load_vector128(constants::mm_lower_idx_chars3, vl); + + const vuint8m1_t lower1 = __riscv_vrgather_vv_u8m1(chars1, lower_idx1, vl); + const vuint8m1_t lower2 = __riscv_vrgather_vv_u8m1(chars2, lower_idx2, vl); + const vuint8m1_t lower3 = __riscv_vrgather_vv_u8m1(chars3, lower_idx3, vl); + + lower = __riscv_vslideup_vx_u8m1(lower, lower1, 0, vl); + lower = __riscv_vslideup_vx_u8m1(lower, lower2, 7, vl); + lower = __riscv_vslideup_vx_u8m1(lower, lower3, 14, vl); + } + + // Convert characters to 8-bit integers. See the comment in from_chars_x86.hpp for the algorithm description. + const vint8m1_t upper_i8 = __riscv_vreinterpret_v_u8m1_i8m1(upper); + const vint8m1_t lower_i8 = __riscv_vreinterpret_v_u8m1_i8m1(lower); + const vint8m1_t char_code1_cmp_i8 = __riscv_vreinterpret_v_u8m1_i8m1(char_code1_cmp); + const vint8m1_t char_code2_cmp_i8 = __riscv_vreinterpret_v_u8m1_i8m1(char_code2_cmp); + + const vbool8_t upper_code2_mask = __riscv_vmsgt_vv_i8m1_b8(upper_i8, char_code2_cmp_i8, vl); + const vbool8_t upper_code1_mask = __riscv_vmsgt_vv_i8m1_b8(upper_i8, char_code1_cmp_i8, vl); + const vbool8_t lower_code2_mask = __riscv_vmsgt_vv_i8m1_b8(lower_i8, char_code2_cmp_i8, vl); + const vbool8_t lower_code1_mask = __riscv_vmsgt_vv_i8m1_b8(lower_i8, char_code1_cmp_i8, vl); + + vuint8m1_t upper_sub = __riscv_vmerge_vvm_u8m1(char_code1_sub, char_code2_sub, upper_code2_mask, vl); + upper_sub = __riscv_vmerge_vvm_u8m1(char_code0_sub, upper_sub, upper_code1_mask, vl); + + vuint8m1_t lower_sub = __riscv_vmerge_vvm_u8m1(char_code1_sub, char_code2_sub, lower_code2_mask, vl); + lower_sub = __riscv_vmerge_vvm_u8m1(char_code0_sub, lower_sub, lower_code1_mask, vl); + + upper = __riscv_vsub_vv_u8m1(upper, upper_sub, vl); + lower = __riscv_vsub_vv_u8m1(lower, lower_sub, vl); + + // Check hexadecimal character validity. Values outside 0-15 will have non-zero upper 4 bits. + const vuint8m1_t mm_F0 = load_vector128(constants::mm_F0, vl); + const vuint8m1_t upper_hi_bits = __riscv_vand_vv_u8m1(upper, mm_F0, vl); + const vuint8m1_t lower_hi_bits = __riscv_vand_vv_u8m1(lower, mm_F0, vl); + + const vbool8_t upper_valid = __riscv_vmseq_vx_u8m1_b8(upper_hi_bits, 0, vl); + const vbool8_t lower_valid = __riscv_vmseq_vx_u8m1_b8(lower_hi_bits, 0, vl); + + const vbool8_t upper_invalid = __riscv_vmnand_mm_b8(upper_valid, upper_valid, vl); + const vbool8_t lower_invalid = __riscv_vmnand_mm_b8(lower_valid, lower_valid, vl); + + const long first_invalid_upper = __riscv_vfirst_m_b8(upper_invalid, vl); + const long first_invalid_lower = __riscv_vfirst_m_b8(lower_invalid, vl); + + if (BOOST_UNLIKELY(first_invalid_upper >= 0 || first_invalid_lower >= 0)) + { + unsigned int pos_upper = 0xFFFFFFFFu; + unsigned int pos_lower = 0xFFFFFFFFu; + if (first_invalid_upper >= 0) + { + pos_upper = static_cast< unsigned int >(first_invalid_upper) * 2u; + } + if (first_invalid_lower >= 0) + { + pos_lower = static_cast< unsigned int >(first_invalid_lower) * 2u + 1u; + } + unsigned int pos = pos_upper < pos_lower ? pos_upper : pos_lower; + if (pos >= 8u) + { + unsigned int dash_count = (pos - 4u) / 4u; + if (dash_count > 4u) + dash_count = 4u; + pos += dash_count; + } + + if (pos < end_pos) + { + end_pos = pos; + ec = from_chars_error::hex_digit_expected; + } + } + else if (BOOST_LIKELY(ec == from_chars_error::none)) + { + const vuint8m1_t result = __riscv_vor_vv_u8m1( + __riscv_vsll_vx_u8m1(upper, 4, vl), + lower, + vl); + __riscv_vse8_v_u8m1(data, result, vl); + } +} + +template< typename Char > +BOOST_FORCEINLINE from_chars_result< Char > from_chars_simd(const Char* begin, const Char* end, uuid& u) noexcept +{ + static_assert(sizeof(Char) == 1u || sizeof(Char) == 2u || sizeof(Char) == 4u, "Boost.UUID: Unsupported input character type for from_chars"); + + using char_constants = uuids::detail::from_chars_simd_char_constants< Char >; + + const size_t vl = __riscv_vsetvl_e8m1(16); + + unsigned int end_pos = 36u; + from_chars_error ec = from_chars_error::none; + vuint8m1_t chars1, chars2, chars3; + + if (BOOST_LIKELY((end - begin) >= 36)) + { + chars1 = from_chars_simd_load_traits< Char >::load_packed_16(begin, vl); + chars2 = from_chars_simd_load_traits< Char >::load_packed_16(begin + 16, vl); + chars3 = from_chars_simd_load_traits< Char >::load_packed_4(begin + 32, vl); + } + else + { + end_pos = static_cast< unsigned int >(end - begin); + ec = from_chars_error::unexpected_end_of_input; + + const Char* p = begin; + unsigned int n = static_cast< unsigned int >(end - begin); + if (n >= 16u) + { + chars1 = from_chars_simd_load_traits< Char >::load_packed_16(p, vl); + p += 16; + n -= 16u; + } + else + { + chars1 = from_chars_simd_load_traits< Char >::load_packed_n(p, n, vl); + p += n; + n = 0u; + } + + if (n >= 16u) + { + chars2 = from_chars_simd_load_traits< Char >::load_packed_16(p, vl); + p += 16; + n -= 16u; + } + else + { + chars2 = from_chars_simd_load_traits< Char >::load_packed_n(p, n, vl); + p += n; + n = 0u; + } + + chars3 = from_chars_simd_load_traits< Char >::load_packed_n(p, n, vl); + } + + from_chars_simd_core + ( + chars1, chars2, chars3, + load_vector128(char_constants::mm_expected_dashes1, vl), + load_vector128(char_constants::mm_expected_dashes2, vl), + load_vector128(char_constants::mm_char_code1_cmp, vl), + load_vector128(char_constants::mm_char_code2_cmp, vl), + load_vector128(char_constants::mm_char_code0_sub, vl), + load_vector128(char_constants::mm_char_code1_sub, vl), + load_vector128(char_constants::mm_char_code2_sub, vl), + u.data(), end_pos, ec, vl + ); + + return { begin + end_pos, ec }; +} + +} // namespace detail +} // namespace uuids +} // namespace boost + +#endif // defined(BOOST_UUID_USE_RISCV_V) + +#endif // BOOST_UUID_DETAIL_FROM_CHARS_RISCV_HPP_INCLUDED +