2
0
mirror of https://github.com/boostorg/uuid.git synced 2026-01-19 04:42:16 +00:00

Merge pull request #186 from Lastique/feature/from_chars_simd

Add SIMD implementation of `from_chars`
This commit is contained in:
Peter Dimov
2026-01-05 15:53:07 +02:00
committed by GitHub
19 changed files with 1444 additions and 265 deletions

View File

@@ -116,6 +116,14 @@ jobs:
container: ubuntu:24.04
install: g++-13-multilib
address-model: 32,64
- toolset: gcc-13
cxxstd: "11,14,17,20,2b"
instruction-set: rocketlake
cpu-requirements: [ avx512f, avx512cd, avx512vl, avx512dq, avx512bw, avx512vbmi, avx512_vbmi2, avx512_bitalg, bmi1, bmi2 ]
os: ubuntu-latest
container: ubuntu:24.04
install: g++-13-multilib
address-model: 32,64
- toolset: clang
compiler: clang++-3.9
cxxstd: "11,14"
@@ -254,6 +262,14 @@ jobs:
container: ubuntu:24.04
os: ubuntu-latest
install: clang-17
- toolset: clang
compiler: clang++-17
cxxstd: "11,14,17,20,2b"
instruction-set: rocketlake
cpu-requirements: [ avx512f, avx512cd, avx512vl, avx512dq, avx512bw, avx512vbmi, avx512_vbmi2, avx512_bitalg, bmi1, bmi2 ]
container: ubuntu:24.04
os: ubuntu-latest
install: clang-17
- toolset: clang
os: macos-14
cxxstd: "11,14,17,20,2b"

View File

@@ -32,15 +32,18 @@ However, there are a few options that can be enabled by defining macros prior to
|If defined, enables optimizations for https://en.wikipedia.org/wiki/SSE4#SSE4.1[SSE4.1] extensions available in x86 processors.
|`BOOST_UUID_USE_AVX`
|If defined, enables optimizations for https://en.wikipedia.org/wiki/Advanced_Vector_Extensions[AVX] extensions available in modern x86 processors.
|If defined, enables optimizations for https://en.wikipedia.org/wiki/Advanced_Vector_Extensions[AVX] extensions available in x86 processors.
|`BOOST_UUID_USE_AVX2`
|If defined, enables optimizations for https://en.wikipedia.org/wiki/Advanced_Vector_Extensions#Advanced_Vector_Extensions_2[AVX2] extensions available in modern x86 processors.
|If defined, enables optimizations for https://en.wikipedia.org/wiki/Advanced_Vector_Extensions#Advanced_Vector_Extensions_2[AVX2] extensions available in x86 processors.
|`BOOST_UUID_USE_AVX512_V1`
|If defined, enables optimizations for https://en.wikipedia.org/wiki/AVX-512[AVX-512] F, VL, CD, BW and DQ extensions available in x86 processors (e.g. in Intel Skylake-X).
|`BOOST_UUID_USE_AVX10_1`
|If defined, enables optimizations for https://en.wikipedia.org/wiki/AVX-512[AVX-512] and https://en.wikipedia.org/wiki/Advanced_Vector_Extensions#AVX10[AVX10.1] extensions available in modern x86 processors.
When defined by user, this macro indicates support for the full set of instructions defined in AVX10.1. Currently, the library does not require 512-bit vectors and is compatible with CPUs implementing AVX-512F,
CD, VL, BW and DQ instruction subsets (i.e. equivalent to Intel Skylake-X), so it may auto-detect and use AVX-512 even if only those subsets are supported.
|If defined, enables optimizations for https://en.wikipedia.org/wiki/AVX-512[AVX-512] and https://en.wikipedia.org/wiki/Advanced_Vector_Extensions#AVX10[AVX10.1] extensions available in x86 processors.
When defined by user, this macro indicates support for the full set of instructions defined in AVX10.1. When auto-detected by the library, this macro may be defined even when not all AVX10.1 subsets
are enabled, but rather when the detected subset is sufficient for the library.
|===

View File

@@ -15,8 +15,9 @@
#include <boost/uuid/detail/static_assert.hpp>
#include <boost/config.hpp>
#include <string>
#include <cstddef>
#include <cstdint>
#include <cstring> // for strlen, wcslen
#include <cstring> // for memcpy
namespace boost {
namespace uuids {

View File

@@ -48,7 +48,11 @@
#define BOOST_UUID_USE_AVX2
#endif
#if ((defined(__AVX512F__) && defined(__AVX512VL__) && defined(__AVX512BW__)) || defined(__AVX10_1__)) && !defined(BOOST_UUID_USE_AVX10_1)
#if (defined(__AVX512F__) && defined(__AVX512VL__) && defined(__AVX512BW__) && defined(__AVX512DQ__) && defined(__AVX512CD__)) && !defined(BOOST_UUID_USE_AVX512_V1)
#define BOOST_UUID_USE_AVX512_V1
#endif
#if ((defined(BOOST_UUID_USE_AVX512_V1) && defined(__AVX512VBMI__)) || defined(__AVX10_1__)) && !defined(BOOST_UUID_USE_AVX10_1)
#define BOOST_UUID_USE_AVX10_1
#endif
@@ -66,14 +70,22 @@
#define BOOST_UUID_USE_AVX2
#endif
#if ((defined(__AVX512F__) && defined(__AVX512VL__) && defined(__AVX512BW__)) || defined(__AVX10_1__)) && !defined(BOOST_UUID_USE_AVX10_1)
#if (defined(__AVX512F__) && defined(__AVX512VL__) && defined(__AVX512BW__) && defined(__AVX512DQ__) && defined(__AVX512CD__)) && !defined(BOOST_UUID_USE_AVX512_V1)
#define BOOST_UUID_USE_AVX512_V1
#endif
#if ((defined(BOOST_UUID_USE_AVX512_V1) && defined(__AVX512VBMI__)) || defined(__AVX10_1__)) && !defined(BOOST_UUID_USE_AVX10_1)
#define BOOST_UUID_USE_AVX10_1
#endif
#endif
// More advanced ISA extensions imply less advanced are also available
#if !defined(BOOST_UUID_USE_AVX2) && defined(BOOST_UUID_USE_AVX10_1)
#if !defined(BOOST_UUID_USE_AVX512_V1) && defined(BOOST_UUID_USE_AVX10_1)
#define BOOST_UUID_USE_AVX512_V1
#endif
#if !defined(BOOST_UUID_USE_AVX2) && defined(BOOST_UUID_USE_AVX512_V1)
#define BOOST_UUID_USE_AVX2
#endif
@@ -99,6 +111,7 @@
#if !defined(BOOST_UUID_NO_SIMD) && \
!defined(BOOST_UUID_USE_AVX10_1) && \
!defined(BOOST_UUID_USE_AVX512_V1) && \
!defined(BOOST_UUID_USE_AVX2) && \
!defined(BOOST_UUID_USE_AVX) && \
!defined(BOOST_UUID_USE_SSE41) && \

View File

@@ -7,16 +7,53 @@
#include <boost/uuid/detail/is_constant_evaluated.hpp>
#include <boost/config.hpp>
#include <cstring>
#include <cstddef>
#if defined(__has_builtin)
#if __has_builtin(__builtin_memcpy)
#define BOOST_UUID_DETAIL_HAS_BUILTIN_MEMCPY
#endif
#if __has_builtin(__builtin_memcmp)
#define BOOST_UUID_DETAIL_HAS_BUILTIN_MEMCMP
#endif
#elif defined(BOOST_GCC)
#define BOOST_UUID_DETAIL_HAS_BUILTIN_MEMCPY
#define BOOST_UUID_DETAIL_HAS_BUILTIN_MEMCMP
#endif
#if defined(BOOST_UUID_DETAIL_HAS_BUILTIN_MEMCPY)
#define BOOST_UUID_DETAIL_MEMCPY __builtin_memcpy
#else
#define BOOST_UUID_DETAIL_MEMCPY std::memcpy
#endif
#if defined(BOOST_UUID_DETAIL_HAS_BUILTIN_MEMCMP)
#define BOOST_UUID_DETAIL_MEMCMP __builtin_memcmp
#else
#define BOOST_UUID_DETAIL_MEMCMP std::memcmp
#endif
#if !defined(BOOST_UUID_DETAIL_HAS_BUILTIN_MEMCPY) || !defined(BOOST_UUID_DETAIL_HAS_BUILTIN_MEMCMP)
#include <cstring>
#endif
namespace boost {
namespace uuids {
namespace detail {
// memcpy
BOOST_CXX14_CONSTEXPR inline void memcpy_cx( unsigned char* dest, unsigned char const* src, std::size_t n )
// Note: The function below is a template to prevent an early check whether the function body can ever be evaluated in the context of a constant expression.
// It can't, and that causes compilation errors with clang. The function must still be marked as constexpr to be able to call them in other
// functions that are constexpr, even if such calls are never evaluated in a constant expression. Otherwise, gcc 5 through 8 gets upset.
template< typename = void >
BOOST_CXX14_CONSTEXPR BOOST_FORCEINLINE void memcpy( void* dest, void const* src, std::size_t n ) noexcept
{
BOOST_UUID_DETAIL_MEMCPY( dest, src, n );
}
BOOST_CXX14_CONSTEXPR inline void memcpy_cx( unsigned char* dest, unsigned char const* src, std::size_t n ) noexcept
{
if( is_constant_evaluated_cx() )
{
@@ -24,11 +61,11 @@ BOOST_CXX14_CONSTEXPR inline void memcpy_cx( unsigned char* dest, unsigned char
}
else
{
std::memcpy( dest, src, n );
BOOST_UUID_DETAIL_MEMCPY( dest, src, n );
}
}
BOOST_UUID_CXX14_CONSTEXPR_RT inline void memcpy_rt( unsigned char* dest, unsigned char const* src, std::size_t n )
BOOST_UUID_CXX14_CONSTEXPR_RT inline void memcpy_rt( unsigned char* dest, unsigned char const* src, std::size_t n ) noexcept
{
if( is_constant_evaluated_rt() )
{
@@ -36,13 +73,13 @@ BOOST_UUID_CXX14_CONSTEXPR_RT inline void memcpy_rt( unsigned char* dest, unsign
}
else
{
std::memcpy( dest, src, n );
BOOST_UUID_DETAIL_MEMCPY( dest, src, n );
}
}
// memcmp
BOOST_CXX14_CONSTEXPR inline int memcmp_cx( unsigned char const* s1, unsigned char const* s2, std::size_t n )
BOOST_CXX14_CONSTEXPR inline int memcmp_cx( unsigned char const* s1, unsigned char const* s2, std::size_t n ) noexcept
{
if( is_constant_evaluated_cx() )
{
@@ -56,11 +93,11 @@ BOOST_CXX14_CONSTEXPR inline int memcmp_cx( unsigned char const* s1, unsigned ch
}
else
{
return std::memcmp( s1, s2, n );
return BOOST_UUID_DETAIL_MEMCMP( s1, s2, n );
}
}
BOOST_UUID_CXX14_CONSTEXPR_RT inline int memcmp_rt( unsigned char const* s1, unsigned char const* s2, std::size_t n )
BOOST_UUID_CXX14_CONSTEXPR_RT inline int memcmp_rt( unsigned char const* s1, unsigned char const* s2, std::size_t n ) noexcept
{
if( is_constant_evaluated_rt() )
{
@@ -74,10 +111,13 @@ BOOST_UUID_CXX14_CONSTEXPR_RT inline int memcmp_rt( unsigned char const* s1, uns
}
else
{
return std::memcmp( s1, s2, n );
return BOOST_UUID_DETAIL_MEMCMP( s1, s2, n );
}
}
}}} // namespace boost::uuids::detail
#undef BOOST_UUID_DETAIL_MEMCMP
#undef BOOST_UUID_DETAIL_MEMCPY
#endif // #ifndef BOOST_UUID_DETAIL_CSTRING_INCLUDED

View File

@@ -6,8 +6,8 @@
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/uuid/detail/is_constant_evaluated.hpp>
#include <boost/uuid/detail/cstring.hpp>
#include <boost/config.hpp>
#include <cstring>
#include <cstdint>
#if defined(_MSC_VER) && !defined(__clang__)
@@ -116,14 +116,14 @@ BOOST_CXX14_CONSTEXPR inline __uint128_t byteswap( __uint128_t x ) noexcept
inline std::uint16_t load_native_u16( void const* p ) noexcept
{
std::uint16_t tmp;
std::memcpy( &tmp, p, sizeof( tmp ) );
detail::memcpy( &tmp, p, sizeof( tmp ) );
return tmp;
}
inline std::uint16_t load_little_u16( void const* p ) noexcept
{
std::uint16_t tmp;
std::memcpy( &tmp, p, sizeof( tmp ) );
detail::memcpy( &tmp, p, sizeof( tmp ) );
#if BOOST_UUID_BYTE_ORDER == BOOST_UUID_ORDER_LITTLE_ENDIAN
@@ -144,7 +144,7 @@ BOOST_CXX14_CONSTEXPR inline std::uint16_t load_big_u16( unsigned char const* p
}
std::uint16_t tmp = {};
std::memcpy( &tmp, p, sizeof( tmp ) );
detail::memcpy( &tmp, p, sizeof( tmp ) );
#if BOOST_UUID_BYTE_ORDER == BOOST_UUID_ORDER_BIG_ENDIAN
@@ -162,7 +162,7 @@ BOOST_CXX14_CONSTEXPR inline std::uint16_t load_big_u16( unsigned char const* p
inline std::uint32_t load_native_u32( void const* p ) noexcept
{
std::uint32_t tmp;
std::memcpy( &tmp, p, sizeof( tmp ) );
detail::memcpy( &tmp, p, sizeof( tmp ) );
return tmp;
}
@@ -179,7 +179,7 @@ BOOST_CXX14_CONSTEXPR inline std::uint32_t load_little_u32( unsigned char const*
}
std::uint32_t tmp = {};
std::memcpy( &tmp, p, sizeof( tmp ) );
detail::memcpy( &tmp, p, sizeof( tmp ) );
#if BOOST_UUID_BYTE_ORDER == BOOST_UUID_ORDER_LITTLE_ENDIAN
@@ -205,7 +205,7 @@ BOOST_CXX14_CONSTEXPR inline std::uint32_t load_big_u32( unsigned char const* p
}
std::uint32_t tmp = {};
std::memcpy( &tmp, p, sizeof( tmp ) );
detail::memcpy( &tmp, p, sizeof( tmp ) );
#if BOOST_UUID_BYTE_ORDER == BOOST_UUID_ORDER_BIG_ENDIAN
@@ -223,14 +223,14 @@ BOOST_CXX14_CONSTEXPR inline std::uint32_t load_big_u32( unsigned char const* p
inline std::uint64_t load_native_u64( void const* p ) noexcept
{
std::uint64_t tmp = {};
std::memcpy( &tmp, p, sizeof( tmp ) );
detail::memcpy( &tmp, p, sizeof( tmp ) );
return tmp;
}
inline std::uint64_t load_little_u64( void const* p ) noexcept
{
std::uint64_t tmp;
std::memcpy( &tmp, p, sizeof( tmp ) );
detail::memcpy( &tmp, p, sizeof( tmp ) );
#if BOOST_UUID_BYTE_ORDER == BOOST_UUID_ORDER_LITTLE_ENDIAN
@@ -260,7 +260,7 @@ BOOST_CXX14_CONSTEXPR inline std::uint64_t load_big_u64( unsigned char const* p
}
std::uint64_t tmp = {};
std::memcpy( &tmp, p, sizeof( tmp ) );
detail::memcpy( &tmp, p, sizeof( tmp ) );
#if BOOST_UUID_BYTE_ORDER == BOOST_UUID_ORDER_BIG_ENDIAN
@@ -280,14 +280,14 @@ BOOST_CXX14_CONSTEXPR inline std::uint64_t load_big_u64( unsigned char const* p
inline __uint128_t load_native_u128( void const* p ) noexcept
{
__uint128_t tmp = {};
std::memcpy( &tmp, p, sizeof( tmp ) );
detail::memcpy( &tmp, p, sizeof( tmp ) );
return tmp;
}
inline __uint128_t load_little_u128( void const* p ) noexcept
{
__uint128_t tmp;
std::memcpy( &tmp, p, sizeof( tmp ) );
detail::memcpy( &tmp, p, sizeof( tmp ) );
#if BOOST_UUID_BYTE_ORDER == BOOST_UUID_ORDER_LITTLE_ENDIAN
@@ -303,7 +303,7 @@ inline __uint128_t load_little_u128( void const* p ) noexcept
inline __uint128_t load_big_u128( void const* p ) noexcept
{
__uint128_t tmp = {};
std::memcpy( &tmp, p, sizeof( tmp ) );
detail::memcpy( &tmp, p, sizeof( tmp ) );
#if BOOST_UUID_BYTE_ORDER == BOOST_UUID_ORDER_BIG_ENDIAN
@@ -322,7 +322,7 @@ inline __uint128_t load_big_u128( void const* p ) noexcept
inline void store_native_u16( void* p, std::uint16_t v ) noexcept
{
std::memcpy( p, &v, sizeof( v ) );
detail::memcpy( p, &v, sizeof( v ) );
}
inline void store_little_u16( void* p, std::uint16_t v ) noexcept
@@ -333,7 +333,7 @@ inline void store_little_u16( void* p, std::uint16_t v ) noexcept
#endif
std::memcpy( p, &v, sizeof( v ) );
detail::memcpy( p, &v, sizeof( v ) );
}
inline void store_big_u16( void* p, std::uint16_t v ) noexcept
@@ -344,14 +344,14 @@ inline void store_big_u16( void* p, std::uint16_t v ) noexcept
#endif
std::memcpy( p, &v, sizeof( v ) );
detail::memcpy( p, &v, sizeof( v ) );
}
// store_*_u32
inline void store_native_u32( void* p, std::uint32_t v ) noexcept
{
std::memcpy( p, &v, sizeof( v ) );
detail::memcpy( p, &v, sizeof( v ) );
}
inline void store_little_u32( void* p, std::uint32_t v ) noexcept
@@ -362,7 +362,7 @@ inline void store_little_u32( void* p, std::uint32_t v ) noexcept
#endif
std::memcpy( p, &v, sizeof( v ) );
detail::memcpy( p, &v, sizeof( v ) );
}
inline void store_big_u32( void* p, std::uint32_t v ) noexcept
@@ -373,14 +373,14 @@ inline void store_big_u32( void* p, std::uint32_t v ) noexcept
#endif
std::memcpy( p, &v, sizeof( v ) );
detail::memcpy( p, &v, sizeof( v ) );
}
// store_*_u64
inline void store_native_u64( void* p, std::uint64_t v ) noexcept
{
std::memcpy( p, &v, sizeof( v ) );
detail::memcpy( p, &v, sizeof( v ) );
}
inline void store_little_u64( void* p, std::uint64_t v ) noexcept
@@ -391,7 +391,7 @@ inline void store_little_u64( void* p, std::uint64_t v ) noexcept
#endif
std::memcpy( p, &v, sizeof( v ) );
detail::memcpy( p, &v, sizeof( v ) );
}
inline void store_big_u64( void* p, std::uint64_t v ) noexcept
@@ -402,7 +402,7 @@ inline void store_big_u64( void* p, std::uint64_t v ) noexcept
#endif
std::memcpy( p, &v, sizeof( v ) );
detail::memcpy( p, &v, sizeof( v ) );
}
// store_*_u128
@@ -411,7 +411,7 @@ inline void store_big_u64( void* p, std::uint64_t v ) noexcept
inline void store_native_u128( void* p, __uint128_t v ) noexcept
{
std::memcpy( p, &v, sizeof( v ) );
detail::memcpy( p, &v, sizeof( v ) );
}
inline void store_little_u128( void* p, __uint128_t v ) noexcept
@@ -422,7 +422,7 @@ inline void store_little_u128( void* p, __uint128_t v ) noexcept
#endif
std::memcpy( p, &v, sizeof( v ) );
detail::memcpy( p, &v, sizeof( v ) );
}
inline void store_big_u128( void* p, __uint128_t v ) noexcept
@@ -433,7 +433,7 @@ inline void store_big_u128( void* p, __uint128_t v ) noexcept
#endif
std::memcpy( p, &v, sizeof( v ) );
detail::memcpy( p, &v, sizeof( v ) );
}
#endif

View File

@@ -6,167 +6,33 @@
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/uuid/uuid.hpp>
#include <boost/config.hpp>
#include <boost/uuid/detail/config.hpp>
#include <boost/uuid/detail/is_constant_evaluated.hpp>
#include <boost/uuid/detail/from_chars_result.hpp>
#include <boost/uuid/detail/from_chars_generic.hpp>
#if defined(BOOST_UUID_USE_SSE41)
#include <boost/uuid/detail/from_chars_x86.hpp>
#endif
namespace boost {
namespace uuids {
namespace detail {
// 0-9, A-F, a-f are consecutive in both ASCII and EBCDIC
constexpr char const* from_chars_digits( char const* ) noexcept
{
return "09AFaf-{}";
}
constexpr wchar_t const* from_chars_digits( wchar_t const* ) noexcept
{
return L"09AFaf-{}";
}
constexpr char16_t const* from_chars_digits( char16_t const* ) noexcept
{
return u"09AFaf-{}";
}
constexpr char32_t const* from_chars_digits( char32_t const* ) noexcept
{
return U"09AFaf-{}";
}
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
constexpr char8_t const* from_chars_digits( char8_t const* ) noexcept
{
return u8"09AFaf-{}";
}
#endif
template<class Ch>
BOOST_CXX14_CONSTEXPR inline
unsigned char from_chars_digit_value( Ch ch ) noexcept
{
constexpr Ch const* digits = detail::from_chars_digits( static_cast<Ch const*>( nullptr ) );
if( ch >= digits[ 0 ] && ch <= digits[ 1 ] )
{
return static_cast<unsigned char>( ch - digits[ 0 ] );
}
if( ch >= digits[ 2 ] && ch <= digits[ 3 ] )
{
return static_cast<unsigned char>( ch - digits[ 2 ] + 10 );
}
if( ch >= digits[ 4 ] && ch <= digits[ 5 ] )
{
return static_cast<unsigned char>( ch - digits[ 4 ] + 10 );
}
return 255;
}
template<class Ch>
BOOST_CXX14_CONSTEXPR inline
bool from_chars_is_dash( Ch ch ) noexcept
{
constexpr Ch const* digits = detail::from_chars_digits( static_cast<Ch const*>( nullptr ) );
return ch == digits[ 6 ];
}
template<class Ch>
BOOST_CXX14_CONSTEXPR inline
bool from_chars_is_opening_brace( Ch ch ) noexcept
{
constexpr Ch const* digits = detail::from_chars_digits( static_cast<Ch const*>( nullptr ) );
return ch == digits[ 7 ];
}
template<class Ch>
BOOST_CXX14_CONSTEXPR inline
bool from_chars_is_closing_brace( Ch ch ) noexcept
{
constexpr Ch const* digits = detail::from_chars_digits( static_cast<Ch const*>( nullptr ) );
return ch == digits[ 8 ];
}
} // namespace detail
enum class from_chars_error
{
none = 0,
unexpected_end_of_input,
hex_digit_expected,
dash_expected,
closing_brace_expected,
unexpected_extra_input
};
template<class Ch> struct from_chars_result
{
Ch const* ptr;
from_chars_error ec;
constexpr explicit operator bool() const noexcept { return ec == from_chars_error::none; }
};
template<class Ch>
BOOST_CXX14_CONSTEXPR inline
BOOST_UUID_CXX14_CONSTEXPR_RT inline
from_chars_result<Ch> from_chars( Ch const* first, Ch const* last, uuid& u ) noexcept
{
u = {};
for( std::size_t i = 0; i < 16; ++i )
#if defined(BOOST_UUID_USE_SSE41)
if( detail::is_constant_evaluated_rt() )
{
if( first == last )
{
return { first, from_chars_error::unexpected_end_of_input };
}
unsigned char v1 = detail::from_chars_digit_value( *first );
if( v1 == 255 )
{
return { first, from_chars_error::hex_digit_expected };
}
++first;
if( first == last )
{
return { first, from_chars_error::unexpected_end_of_input };
}
unsigned char v2 = detail::from_chars_digit_value( *first );
if( v2 == 255 )
{
return { first, from_chars_error::hex_digit_expected };
}
++first;
u.data()[ i ] = static_cast<unsigned char>( ( v1 << 4 ) + v2 );
if( i == 3 || i == 5 || i == 7 || i == 9 )
{
if( first == last )
{
return { first, from_chars_error::unexpected_end_of_input };
}
if( !detail::from_chars_is_dash( *first ) )
{
return { first, from_chars_error::dash_expected };
}
++first;
}
return detail::from_chars_generic( first, last, u );
}
return { first, from_chars_error::none };
else
{
return detail::from_chars_simd( first, last, u );
}
#else
return detail::from_chars_generic( first, last, u );
#endif
}
}} //namespace boost::uuids

View File

@@ -0,0 +1,162 @@
#ifndef BOOST_UUID_DETAIL_FROM_CHARS_GENERIC_HPP_INCLUDED
#define BOOST_UUID_DETAIL_FROM_CHARS_GENERIC_HPP_INCLUDED
// Copyright 2025 Peter Dimov
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <cstddef>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/detail/from_chars_result.hpp>
#include <boost/config.hpp>
#if defined(BOOST_UUID_REPORT_IMPLEMENTATION)
#include <boost/config/pragma_message.hpp>
BOOST_PRAGMA_MESSAGE( "Using from_chars_generic.hpp" )
#endif
namespace boost {
namespace uuids {
namespace detail {
// 0-9, A-F, a-f are consecutive in both ASCII and EBCDIC
constexpr char const* from_chars_digits( char const* ) noexcept
{
return "09AFaf-{}";
}
constexpr wchar_t const* from_chars_digits( wchar_t const* ) noexcept
{
return L"09AFaf-{}";
}
constexpr char16_t const* from_chars_digits( char16_t const* ) noexcept
{
return u"09AFaf-{}";
}
constexpr char32_t const* from_chars_digits( char32_t const* ) noexcept
{
return U"09AFaf-{}";
}
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
constexpr char8_t const* from_chars_digits( char8_t const* ) noexcept
{
return u8"09AFaf-{}";
}
#endif
template<class Ch>
BOOST_CXX14_CONSTEXPR inline
unsigned char from_chars_digit_value( Ch ch ) noexcept
{
constexpr Ch const* digits = detail::from_chars_digits( static_cast<Ch const*>( nullptr ) );
if( ch >= digits[ 0 ] && ch <= digits[ 1 ] )
{
return static_cast<unsigned char>( ch - digits[ 0 ] );
}
if( ch >= digits[ 2 ] && ch <= digits[ 3 ] )
{
return static_cast<unsigned char>( ch - digits[ 2 ] + 10 );
}
if( ch >= digits[ 4 ] && ch <= digits[ 5 ] )
{
return static_cast<unsigned char>( ch - digits[ 4 ] + 10 );
}
return 255;
}
template<class Ch>
BOOST_CXX14_CONSTEXPR inline
bool from_chars_is_dash( Ch ch ) noexcept
{
constexpr Ch const* digits = detail::from_chars_digits( static_cast<Ch const*>( nullptr ) );
return ch == digits[ 6 ];
}
template<class Ch>
BOOST_CXX14_CONSTEXPR inline
bool from_chars_is_opening_brace( Ch ch ) noexcept
{
constexpr Ch const* digits = detail::from_chars_digits( static_cast<Ch const*>( nullptr ) );
return ch == digits[ 7 ];
}
template<class Ch>
BOOST_CXX14_CONSTEXPR inline
bool from_chars_is_closing_brace( Ch ch ) noexcept
{
constexpr Ch const* digits = detail::from_chars_digits( static_cast<Ch const*>( nullptr ) );
return ch == digits[ 8 ];
}
template<class Ch>
BOOST_CXX14_CONSTEXPR inline
from_chars_result<Ch> from_chars_generic( Ch const* first, Ch const* last, uuid& u ) noexcept
{
u = {};
for( std::size_t i = 0; i < 16; ++i )
{
if( first == last )
{
return { first, from_chars_error::unexpected_end_of_input };
}
unsigned char v1 = detail::from_chars_digit_value( *first );
if( v1 == 255 )
{
return { first, from_chars_error::hex_digit_expected };
}
++first;
if( first == last )
{
return { first, from_chars_error::unexpected_end_of_input };
}
unsigned char v2 = detail::from_chars_digit_value( *first );
if( v2 == 255 )
{
return { first, from_chars_error::hex_digit_expected };
}
++first;
u.data()[ i ] = static_cast<unsigned char>( ( v1 << 4 ) + v2 );
if( i == 3 || i == 5 || i == 7 || i == 9 )
{
if( first == last )
{
return { first, from_chars_error::unexpected_end_of_input };
}
if( !detail::from_chars_is_dash( *first ) )
{
return { first, from_chars_error::dash_expected };
}
++first;
}
}
return { first, from_chars_error::none };
}
}}} //namespace boost::uuids::detail
#endif // BOOST_UUID_DETAIL_TO_CHARS_GENERIC_HPP_INCLUDED

View File

@@ -0,0 +1,32 @@
#ifndef BOOST_UUID_DETAIL_FROM_CHARS_RESULT_HPP_INCLUDED
#define BOOST_UUID_DETAIL_FROM_CHARS_RESULT_HPP_INCLUDED
// Copyright 2025 Peter Dimov
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
namespace boost {
namespace uuids {
enum class from_chars_error
{
none = 0,
unexpected_end_of_input,
hex_digit_expected,
dash_expected,
closing_brace_expected,
unexpected_extra_input
};
template<class Ch> struct from_chars_result
{
Ch const* ptr;
from_chars_error ec;
constexpr explicit operator bool() const noexcept { return ec == from_chars_error::none; }
};
}} //namespace boost::uuids
#endif // BOOST_UUID_DETAIL_FROM_CHARS_RESULT_HPP_INCLUDED

View File

@@ -0,0 +1,952 @@
#ifndef BOOST_UUID_DETAIL_FROM_CHARS_X86_HPP_INCLUDED
#define BOOST_UUID_DETAIL_FROM_CHARS_X86_HPP_INCLUDED
// Copyright 2025 Andrey Semashev
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/uuid/detail/config.hpp>
#if defined(BOOST_UUID_USE_SSE41)
#include <cstdint>
#include <cstring>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/detail/endian.hpp>
#include <boost/uuid/detail/from_chars_result.hpp>
#include <boost/uuid/detail/simd_vector.hpp>
#if defined(BOOST_UUID_REPORT_IMPLEMENTATION)
#include <boost/config/pragma_message.hpp>
#if defined(BOOST_UUID_USE_AVX10_1)
BOOST_PRAGMA_MESSAGE( "Using from_chars_x86.hpp, AVX10.1" )
#elif defined(BOOST_UUID_USE_AVX512_V1)
BOOST_PRAGMA_MESSAGE( "Using from_chars_x86.hpp, AVX512v1" )
#elif defined(BOOST_UUID_USE_AVX)
BOOST_PRAGMA_MESSAGE( "Using from_chars_x86.hpp, AVX" )
#else
BOOST_PRAGMA_MESSAGE( "Using from_chars_x86.hpp, SSE4.1" )
#endif
#endif // #if defined(BOOST_UUID_REPORT_IMPLEMENTATION)
#if defined(BOOST_UUID_USE_AVX)
#include <immintrin.h>
#else
#include <smmintrin.h>
#endif
#if defined(_MSC_VER) && !defined(__clang__)
#include <intrin.h>
#pragma intrinsic(_BitScanForward)
#endif
namespace boost {
namespace uuids {
namespace detail {
//! Returns the number of trailing zero bits in a non-zero input integer
BOOST_FORCEINLINE std::uint32_t countr_zero_nz(std::uint32_t n) noexcept
{
#if defined(__GNUC__) || defined(__clang__)
return __builtin_ctz(n);
#elif defined(_MSC_VER) && !defined(__clang__)
unsigned long index;
_BitScanForward(&index, n);
return static_cast< std::uint32_t >(index);
#else
std::uint32_t index = 0u;
if ((n & 0xFFFF) == 0u)
{
n >>= 16u;
index += 16u;
}
if ((n & 0xFF) == 0u)
{
n >>= 8u;
index += 8u;
}
if ((n & 0xF) == 0u)
{
n >>= 4u;
index += 4u;
}
if ((n & 0x3) == 0u)
{
n >>= 2u;
index += 2u;
}
if ((n & 0x1) == 0u)
{
index += 1u;
}
return index;
#endif
}
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_dashes;
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;
#if defined(BOOST_UUID_USE_AVX512_V1) || !defined(BOOST_UUID_USE_AVX)
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;
#endif // defined(BOOST_UUID_USE_AVX512_V1) || !defined(BOOST_UUID_USE_AVX)
};
template< typename Char, bool IsCharASCIICompatible, bool IsWCharASCIICompatible >
const simd_vector128< std::uint8_t > from_chars_simd_char_constants< Char, IsCharASCIICompatible, IsWCharASCIICompatible >::mm_expected_dashes =
{{ 0x2D, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x2D }}; // 0x2D is '-' in ASCII
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)
}};
#if defined(BOOST_UUID_USE_AVX512_V1) || !defined(BOOST_UUID_USE_AVX)
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
}};
#endif // defined(BOOST_UUID_USE_AVX512_V1) || !defined(BOOST_UUID_USE_AVX)
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_dashes;
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;
#if defined(BOOST_UUID_USE_AVX512_V1) || !defined(BOOST_UUID_USE_AVX)
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;
#endif // defined(BOOST_UUID_USE_AVX512_V1) || !defined(BOOST_UUID_USE_AVX)
};
template< bool IsWCharASCIICompatible >
const simd_vector128< std::uint8_t > from_chars_simd_char_constants< char, false, IsWCharASCIICompatible >::mm_expected_dashes =
{{
static_cast< std::uint8_t >('-'), 0x00, 0x00, 0x00, 0x00, static_cast< std::uint8_t >('-'), 0x00, 0x00,
0x00, 0x00, static_cast< std::uint8_t >('-'), 0x00, 0x00, 0x00, 0x00, static_cast< std::uint8_t >('-')
}};
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)
}};
#if defined(BOOST_UUID_USE_AVX512_V1) || !defined(BOOST_UUID_USE_AVX)
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
}};
#endif // defined(BOOST_UUID_USE_AVX512_V1) || !defined(BOOST_UUID_USE_AVX)
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_dashes;
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;
#if defined(BOOST_UUID_USE_AVX512_V1) || !defined(BOOST_UUID_USE_AVX)
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;
#endif // defined(BOOST_UUID_USE_AVX512_V1) || !defined(BOOST_UUID_USE_AVX)
};
template< bool IsCharASCIICompatible >
const simd_vector128< std::uint8_t > from_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false >::mm_expected_dashes =
{{
static_cast< std::uint8_t >(L'-'), 0x00, 0x00, 0x00, 0x00, static_cast< std::uint8_t >(L'-'), 0x00, 0x00,
0x00, 0x00, static_cast< std::uint8_t >(L'-'), 0x00, 0x00, 0x00, 0x00, static_cast< std::uint8_t >(L'-')
}};
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)
}};
#if defined(BOOST_UUID_USE_AVX512_V1) || !defined(BOOST_UUID_USE_AVX)
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
}};
#endif // defined(BOOST_UUID_USE_AVX512_V1) || !defined(BOOST_UUID_USE_AVX)
template< typename >
struct from_chars_simd_constants
{
static const simd_vector128< std::uint8_t > mm_dashes_mask;
#if defined(BOOST_UUID_USE_AVX10_1) && defined(BOOST_UUID_FROM_CHARS_X86_USE_VPERMI2B)
static const simd_vector128< std::uint8_t > mm_split_half_bytes_pattern1;
static const simd_vector128< std::uint8_t > mm_split_half_bytes_pattern2;
#else
static const simd_vector128< std::uint8_t > mm_split_half_bytes_pattern1;
static const simd_vector128< std::uint8_t > mm_split_half_bytes_pattern2;
static const simd_vector128< std::uint8_t > mm_split_half_bytes_pattern3;
static const simd_vector128< std::uint8_t > mm_split_half_bytes_blend_mask;
#endif
static const simd_vector128< std::uint8_t > mm_F0;
#if defined(BOOST_UUID_USE_AVX) && !defined(BOOST_UUID_USE_AVX512_V1)
static const simd_vector128< std::uint8_t > mm_2;
#endif
};
template< typename T >
const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_dashes_mask =
{{ 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF }};
#if defined(BOOST_UUID_USE_AVX10_1) && defined(BOOST_UUID_FROM_CHARS_X86_USE_VPERMI2B)
template< typename T >
const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_split_half_bytes_pattern1 =
{{ 0x01, 0x03, 0x05, 0x07, 0x0A, 0x0C, 0x0F, 0x11, 0x00, 0x02, 0x04, 0x06, 0x09, 0x0B, 0x0E, 0x10 }};
template< typename T >
const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_split_half_bytes_pattern2 =
{{ 0x04, 0x06, 0x09, 0x0B, 0x0D, 0x0F, 0x11, 0x13, 0x03, 0x05, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12 }};
#else
template< typename T >
const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_split_half_bytes_pattern1 =
{{ 0x01, 0x03, 0x05, 0x07, 0x0A, 0x0C, 0x0F, 0x80, 0x00, 0x02, 0x04, 0x06, 0x09, 0x0B, 0x0E, 0x80 }};
template< typename T >
const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_split_half_bytes_pattern2 =
{{ 0x04, 0x06, 0x09, 0x0B, 0x0D, 0x0F, 0x80, 0x01, 0x03, 0x05, 0x08, 0x0A, 0x0C, 0x0E, 0x80, 0x00 }};
template< typename T >
const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_split_half_bytes_pattern3 =
{{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01, 0x03, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x02 }};
template< typename T >
const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_split_half_bytes_blend_mask =
{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }};
#endif
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 }};
#if defined(BOOST_UUID_USE_AVX) && !defined(BOOST_UUID_USE_AVX512_V1)
template< typename T >
const simd_vector128< std::uint8_t > from_chars_simd_constants< T >::mm_2 =
{{ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 }};
#endif
#if defined(BOOST_GCC) && (BOOST_GCC >= 40600)
#pragma GCC diagnostic push
// array subscript N is outside array bounds of '<array>'
// In the partial loads below, masked loads may be used with pointers beyond the input array of characters.
// In all such instances, the actual loads are prevented by the generated masks, don't generate
// hardware faults and therefore are safe.
#pragma GCC diagnostic ignored "-Warray-bounds"
#endif
template< typename Char, unsigned int Size = sizeof(Char) >
struct from_chars_simd_load_traits;
template< typename Char >
struct from_chars_simd_load_traits< Char, 1u >
{
static BOOST_FORCEINLINE __m128i load_packed_16(const Char* p) noexcept
{
return _mm_loadu_si128(reinterpret_cast< const __m128i* >(p));
}
static BOOST_FORCEINLINE __m128i load_packed_4(const Char* p) noexcept
{
return _mm_cvtsi32_si128(detail::load_native_u32(p));
}
static BOOST_FORCEINLINE __m128i load_packed_n(const Char* p, unsigned int n) noexcept
{
#if defined(BOOST_UUID_USE_AVX512_V1)
return _mm_maskz_loadu_epi8(_cvtu32_mask16((static_cast< std::uint32_t >(1u) << n) - 1u), p);
#else
std::uint32_t chars = 0u;
p += n;
if ((n & 1u) != 0u)
{
p -= 1;
chars = *reinterpret_cast< BOOST_MAY_ALIAS std::uint8_t const* >(p);
}
if ((n & 2u) != 0u)
{
p -= 2;
chars = (chars << 16u) | static_cast< std::uint32_t >(detail::load_native_u16(p));
}
__m128i mm_chars = _mm_cvtsi32_si128(chars);
if ((n & 4u) != 0u)
{
p -= 4;
mm_chars = _mm_unpacklo_epi32(_mm_cvtsi32_si128(detail::load_native_u32(p)), mm_chars);
}
if ((n & 8u) != 0u)
{
p -= 8;
mm_chars = _mm_unpacklo_epi64(_mm_loadl_epi64(reinterpret_cast< const __m128i* >(p)), mm_chars);
}
return mm_chars;
#endif
}
};
template< typename Char >
struct from_chars_simd_load_traits< Char, 2u >
{
static BOOST_FORCEINLINE __m128i load_packed_16(const Char* p) noexcept
{
#if defined(BOOST_UUID_USE_AVX512_V1)
return _mm256_cvtusepi16_epi8(_mm256_loadu_si256(reinterpret_cast< const __m256i* >(p)));
#else
return _mm_packus_epi16(_mm_loadu_si128(reinterpret_cast< const __m128i* >(p)), _mm_loadu_si128(reinterpret_cast< const __m128i* >(p + 8)));
#endif
}
static BOOST_FORCEINLINE __m128i load_packed_4(const Char* p) noexcept
{
#if defined(BOOST_UUID_USE_AVX512_V1)
return _mm_cvtusepi16_epi8(_mm_loadl_epi64(reinterpret_cast< const __m128i* >(p)));
#else
return _mm_packus_epi16(_mm_loadl_epi64(reinterpret_cast< const __m128i* >(p)), _mm_setzero_si128());
#endif
}
static BOOST_FORCEINLINE __m128i load_packed_n(const Char* p, unsigned int n) noexcept
{
#if defined(BOOST_UUID_USE_AVX512_V1)
return _mm256_cvtusepi16_epi8(_mm256_maskz_loadu_epi16(_cvtu32_mask16((static_cast< std::uint32_t >(1u) << n) - 1u), p));
#else
__m128i mm_chars1 = _mm_setzero_si128();
__m128i mm_chars2 = _mm_setzero_si128();
p += n;
if ((n & 1u) != 0u)
{
p -= 1;
mm_chars1 = _mm_cvtsi32_si128(detail::load_native_u16(p));
}
if ((n & 2u) != 0u)
{
p -= 2;
mm_chars1 = _mm_unpacklo_epi32(_mm_cvtsi32_si128(detail::load_native_u32(p)), mm_chars1);
}
if ((n & 4u) != 0u)
{
p -= 4;
mm_chars1 = _mm_unpacklo_epi64(_mm_loadl_epi64(reinterpret_cast< const __m128i* >(p)), mm_chars1);
}
if ((n & 8u) != 0u)
{
p -= 8;
mm_chars2 = mm_chars1;
mm_chars1 = _mm_loadu_si128(reinterpret_cast< const __m128i* >(p));
}
return _mm_packus_epi16(mm_chars1, mm_chars2);
#endif
}
};
template< typename Char >
struct from_chars_simd_load_traits< Char, 4u >
{
static BOOST_FORCEINLINE __m128i load_packed_16(const Char* p) noexcept
{
#if defined(BOOST_UUID_USE_AVX512_V1)
#if defined(BOOST_UUID_TO_FROM_CHARS_X86_USE_ZMM)
// Slower than the 256-bit version below on Intel Golden Cove.
return _mm512_cvtusepi32_epi8(_mm512_loadu_epi32(p));
#else // defined(BOOST_UUID_TO_FROM_CHARS_X86_USE_ZMM)
__m128i mm1 = _mm256_cvtusepi32_epi8(_mm256_loadu_si256(reinterpret_cast< const __m256i* >(p)));
__m128i mm2 = _mm256_cvtusepi32_epi8(_mm256_loadu_si256(reinterpret_cast< const __m256i* >(p + 8)));
return _mm_unpacklo_epi64(mm1, mm2);
#endif // defined(BOOST_UUID_TO_FROM_CHARS_X86_USE_ZMM)
#else
__m128i mm1 = _mm_packus_epi32(_mm_loadu_si128(reinterpret_cast< const __m128i* >(p)), _mm_loadu_si128(reinterpret_cast< const __m128i* >(p + 4)));
__m128i mm2 = _mm_packus_epi32(_mm_loadu_si128(reinterpret_cast< const __m128i* >(p + 8)), _mm_loadu_si128(reinterpret_cast< const __m128i* >(p + 12)));
return _mm_packus_epi16(mm1, mm2);
#endif
}
static BOOST_FORCEINLINE __m128i load_packed_4(const Char* p) noexcept
{
#if defined(BOOST_UUID_USE_AVX512_V1)
return _mm_cvtusepi32_epi8(_mm_loadu_si128(reinterpret_cast< const __m128i* >(p)));
#else
__m128i mm1 = _mm_loadu_si128(reinterpret_cast< const __m128i* >(p));
__m128i mm2 = _mm_setzero_si128();
return _mm_packus_epi16(_mm_packus_epi32(mm1, mm2), mm2);
#endif
}
static BOOST_FORCEINLINE __m128i load_packed_n(const Char* p, unsigned int n) noexcept
{
#if defined(BOOST_UUID_USE_AVX512_V1)
const std::uint32_t mask = (static_cast< std::uint32_t >(1u) << n) - 1u;
#if defined(BOOST_UUID_TO_FROM_CHARS_X86_USE_ZMM)
// Slower than the 256-bit version below on Intel Golden Cove.
return _mm512_cvtusepi32_epi8(_mm512_maskz_loadu_epi32(_cvtu32_mask16(mask), p));
#else // defined(BOOST_UUID_TO_FROM_CHARS_X86_USE_ZMM)
__m128i mm1 = _mm256_cvtusepi32_epi8(_mm256_maskz_loadu_epi32(_cvtu32_mask8(mask & 0xFF), p));
__m128i mm2 = _mm256_cvtusepi32_epi8(_mm256_maskz_loadu_epi32(_cvtu32_mask8(mask >> 8u), p + 8));
return _mm_unpacklo_epi64(mm1, mm2);
#endif // defined(BOOST_UUID_TO_FROM_CHARS_X86_USE_ZMM)
#else
__m128i mm_chars1 = _mm_setzero_si128();
__m128i mm_chars2 = _mm_setzero_si128();
__m128i mm_chars3 = _mm_setzero_si128();
__m128i mm_chars4 = _mm_setzero_si128();
p += n;
if ((n & 1u) != 0u)
{
p -= 1;
mm_chars1 = _mm_cvtsi32_si128(detail::load_native_u32(p));
}
if ((n & 2u) != 0u)
{
p -= 2;
mm_chars1 = _mm_unpacklo_epi64(_mm_loadl_epi64(reinterpret_cast< const __m128i* >(p)), mm_chars1);
}
if ((n & 4u) != 0u)
{
p -= 4;
mm_chars2 = mm_chars1;
mm_chars1 = _mm_loadu_si128(reinterpret_cast< const __m128i* >(p));
}
if ((n & 8u) != 0u)
{
p -= 8;
mm_chars4 = mm_chars3;
mm_chars3 = mm_chars2;
mm_chars1 = _mm_loadu_si128(reinterpret_cast< const __m128i* >(p));
mm_chars2 = _mm_loadu_si128(reinterpret_cast< const __m128i* >(p + 4));
}
mm_chars1 = _mm_packus_epi32(mm_chars1, mm_chars2);
mm_chars3 = _mm_packus_epi32(mm_chars3, mm_chars4);
return _mm_packus_epi16(mm_chars1, mm_chars3);
#endif
}
};
#if defined(BOOST_GCC) && (BOOST_GCC >= 40600)
#pragma GCC diagnostic pop
#endif
/*!
* Converts a string of 36 hexadecimal UUID characters in `mm_charsN` (with the last 4 characters in the lowest 32 bits of `mm_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
(
__m128i mm_chars1, __m128i mm_chars2, __m128i mm_chars3,
__m128i const& mm_expected_dashes,
__m128i const& mm_char_code1_cmp,
__m128i const& mm_char_code2_cmp,
#if defined(BOOST_UUID_USE_AVX512_V1) || !defined(BOOST_UUID_USE_AVX)
__m128i const& mm_char_code0_sub,
__m128i const& mm_char_code1_sub,
__m128i const& mm_char_code2_sub,
#else
std::uint32_t char_code_sub,
#endif
std::uint8_t* data, unsigned int& end_pos, from_chars_error& ec
)
{
using constants = uuids::detail::from_chars_simd_constants< void >;
// mm_chars1 mm_chars2 mm_chars3
// |01234567-89ab-cd|ef-0123-456789ab|cdefXXXXXXXXXXXX|
//
// Check if dashes are in the expected positions
{
// mm_dashes
// |-89ab-cdef-0123-|
__m128i mm_dashes = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(mm_chars1), _mm_castsi128_pd(mm_chars2), _MM_SHUFFLE2(0, 1)));
if (BOOST_UNLIKELY(!_mm_test_all_zeros(_mm_xor_si128(mm_dashes, mm_expected_dashes), constants::mm_dashes_mask)))
{
// Some of the dashes are missing
mm_dashes = _mm_and_si128(mm_dashes, constants::mm_dashes_mask);
std::uint32_t dash_mask = _mm_movemask_epi8(_mm_cmpeq_epi8(mm_dashes, mm_expected_dashes));
unsigned int pos = detail::countr_zero_nz(~dash_mask) + 8u;
if (pos < end_pos)
{
end_pos = pos;
ec = from_chars_error::dash_expected;
}
}
}
// Remove the dashes, deinterleave low and high half-byte digit characters
#if defined(BOOST_UUID_USE_AVX10_1) && defined(BOOST_UUID_FROM_CHARS_X86_USE_VPERMI2B)
// Note: This code path is disabled by default, unless BOOST_UUID_FROM_CHARS_X86_USE_VPERMI2B is defined, because vpermi2b/vpermt2b
// instructions are slow on Intel Golden Cove and older microarchitectures, making the alternative version below faster.
// This code path may still be beneficial on AMD CPUs or when Intel optimizes vpermi2b/vpermt2b.
// mm_chars1: |02468ace13579bdf|
// mm_chars2: |02468ace13579bdf|
mm_chars1 = _mm_permutex2var_epi8(mm_chars1, constants::mm_split_half_bytes_pattern1, mm_chars2);
mm_chars2 = _mm_permutex2var_epi8(mm_chars2, constants::mm_split_half_bytes_pattern2, mm_chars3);
#else
// mm_chars1: |02468acZ13579bdZ|
// mm_chars2: |02468aZe13579bZf|
// mm_chars3: |ZZZZZZceZZZZZZdf|
mm_chars1 = _mm_shuffle_epi8(mm_chars1, constants::mm_split_half_bytes_pattern1);
mm_chars2 = _mm_shuffle_epi8(mm_chars2, constants::mm_split_half_bytes_pattern2);
mm_chars3 = _mm_shuffle_epi8(mm_chars3, constants::mm_split_half_bytes_pattern3);
// mm_chars1: |02468ace13579bdf|
// mm_chars2: |02468ace13579bdf|
// Avoid using vpblendvb, which is slow on Intel
#if defined(BOOST_UUID_USE_AVX512_V1)
mm_chars1 = _mm_ternarylogic_epi64(mm_chars1, mm_chars2, constants::mm_split_half_bytes_blend_mask, 0xD8); // (_MM_TERNLOG_A & ~_MM_TERNLOG_C) | (_MM_TERNLOG_B & _MM_TERNLOG_C)
#elif defined(BOOST_UUID_USE_AVX)
mm_chars1 = _mm_or_si128(mm_chars1, _mm_and_si128(mm_chars2, constants::mm_split_half_bytes_blend_mask));
#else
mm_chars1 = _mm_blendv_epi8(mm_chars1, mm_chars2, constants::mm_split_half_bytes_blend_mask);
#endif
mm_chars2 = _mm_blend_epi16(mm_chars2, mm_chars3, 0x88);
#endif
// Group half-byte digits
__m128i mm_lo = _mm_unpacklo_epi64(mm_chars1, mm_chars2);
__m128i mm_hi = _mm_unpackhi_epi64(mm_chars1, mm_chars2);
// Convert characters to 8-bit integers. The algorithm is basically as follows:
//
// - Order the '0'-'9', 'A'-'F' and 'a'-'f' groups of characters in the order of increasing their character code values. From them, pick the two with
// the highest character codes. E.g. in ASCII, that would be 'A'-'F' and 'a'-'f'. This is handled at compile time in from_chars_simd_char_constants.
// - Let mm_char_code2_cmp be a vector of the smallest character code of the second picked group minus 1 (i.e. 'a' - 1), and mm_char_code1_cmp - that
// of the first picked group ('A' - 1).
// - Compare the input hex characters for being greater than mm_char_code2_cmp and mm_char_code1_cmp. This gives the masks where the input contains
// hexadecimal characters of the two greatest character groups, with the mask for mm_char_code1_cmp always including the one for mm_char_code2_cmp.
// Call those masks mm_char_code1_mask and mm_char_code2_mask.
// - For each of the three groups of characters, have a corresponding vector of subtrahends, such that when it is subtracted from the input character codes,
// the characters in the group are mapped onto the corresponding value in the range 0-15. I.e. these would be '0', 'A' + 10 and 'a' + 10. Those are called
// mm_char_code0_sub, mm_char_code1_sub and mm_char_code2_sub, corresponding to the ordered list of groups of characters.
// - Combine the subtrahends such that for elements where mm_char_code2_mask is non-zero, mm_char_code2_sub is used, otherwise where
// mm_char_code1_mask is non-zero, mm_char_code1_sub is used, otherwise mm_char_code0_sub is used.
// - Subtract the combined subtrahends from the input character codes.
//
// The result will be a vector of bytes, where the values 0-15 correspond the hexadecimal characters on input.
//
// Note that there is one caveat due to the fact that there are only signed byte comparisons until AVX-512. This is a problem if the character encoding has
// hexadecimal character codes with the highest bit set to 1. This is handled in from_chars_simd_char_constants by constructing mm_char_code1 and
// mm_char_code2 in such a way that signed comparisons work as described. We also use signed comparisons in AVX-512 to reuse the same constants.
#if defined(BOOST_UUID_USE_AVX512_V1)
__mmask16 k_char_code2_mask_lo = _mm_cmpgt_epi8_mask(mm_lo, mm_char_code2_cmp);
__mmask16 k_char_code2_mask_hi = _mm_cmpgt_epi8_mask(mm_hi, mm_char_code2_cmp);
__mmask16 k_char_code1_mask_lo = _mm_cmpgt_epi8_mask(mm_lo, mm_char_code1_cmp);
__mmask16 k_char_code1_mask_hi = _mm_cmpgt_epi8_mask(mm_hi, mm_char_code1_cmp);
__m128i mm_char_code_sub_lo = _mm_mask_blend_epi8(k_char_code2_mask_lo, mm_char_code1_sub, mm_char_code2_sub);
__m128i mm_char_code_sub_hi = _mm_mask_blend_epi8(k_char_code2_mask_hi, mm_char_code1_sub, mm_char_code2_sub);
mm_char_code_sub_lo = _mm_mask_blend_epi8(k_char_code1_mask_lo, mm_char_code0_sub, mm_char_code_sub_lo);
mm_char_code_sub_hi = _mm_mask_blend_epi8(k_char_code1_mask_hi, mm_char_code0_sub, mm_char_code_sub_hi);
#elif defined(BOOST_UUID_USE_AVX)
// Unlike the legacy SSE4.1 pblendvb instruction, the VEX-coded vpblendvb is slow on Intel. Use a different approach:
// - Each vpcmpgtb produces a mask, where 0 indicates false and -1 - true.
// - mm_char_code1_mask_* always overlaps with the corresponding mm_char_code2_mask_*, which means adding them
// produces a vector where 0 means none of the vpcmpgtb matched the value, -1 - where mm_char_code1_mask_* matched
// and -2 - where mm_char_code2_mask_* matched.
// - Shift that mask to the positive range by adding 2.
// - Use it as a pattern for vpshufb to place one of the 3 lowest bytes in char_code_sub to the positions corresponding
// to the matched characters. This will be the mm_char_code_sub_* subtrahends.
__m128i mm_char_code2_mask_lo = _mm_cmpgt_epi8(mm_lo, mm_char_code2_cmp);
__m128i mm_char_code2_mask_hi = _mm_cmpgt_epi8(mm_hi, mm_char_code2_cmp);
__m128i mm_char_code1_mask_lo = _mm_cmpgt_epi8(mm_lo, mm_char_code1_cmp);
__m128i mm_char_code1_mask_hi = _mm_cmpgt_epi8(mm_hi, mm_char_code1_cmp);
__m128i mm_char_code_pattern_lo = _mm_add_epi8(mm_char_code1_mask_lo, mm_char_code2_mask_lo);
__m128i mm_char_code_pattern_hi = _mm_add_epi8(mm_char_code1_mask_hi, mm_char_code2_mask_hi);
mm_char_code_pattern_lo = _mm_add_epi8(mm_char_code_pattern_lo, constants::mm_2);
mm_char_code_pattern_hi = _mm_add_epi8(mm_char_code_pattern_hi, constants::mm_2);
const __m128i mm_char_code_sub = _mm_cvtsi32_si128(char_code_sub);
__m128i mm_char_code_sub_lo = _mm_shuffle_epi8(mm_char_code_sub, mm_char_code_pattern_lo);
__m128i mm_char_code_sub_hi = _mm_shuffle_epi8(mm_char_code_sub, mm_char_code_pattern_hi);
#else
__m128i mm_char_code2_mask_lo = _mm_cmpgt_epi8(mm_lo, mm_char_code2_cmp);
__m128i mm_char_code2_mask_hi = _mm_cmpgt_epi8(mm_hi, mm_char_code2_cmp);
__m128i mm_char_code1_mask_lo = _mm_cmpgt_epi8(mm_lo, mm_char_code1_cmp);
__m128i mm_char_code1_mask_hi = _mm_cmpgt_epi8(mm_hi, mm_char_code1_cmp);
__m128i mm_char_code_sub_lo = _mm_blendv_epi8(mm_char_code1_sub, mm_char_code2_sub, mm_char_code2_mask_lo);
__m128i mm_char_code_sub_hi = _mm_blendv_epi8(mm_char_code1_sub, mm_char_code2_sub, mm_char_code2_mask_hi);
mm_char_code_sub_lo = _mm_blendv_epi8(mm_char_code0_sub, mm_char_code_sub_lo, mm_char_code1_mask_lo);
mm_char_code_sub_hi = _mm_blendv_epi8(mm_char_code0_sub, mm_char_code_sub_hi, mm_char_code1_mask_hi);
#endif
mm_lo = _mm_sub_epi8(mm_lo, mm_char_code_sub_lo);
mm_hi = _mm_sub_epi8(mm_hi, mm_char_code_sub_hi);
// Check hexadecimal character validity. Proper hexadecimal characters always convert to values of 0-15 and any other characters convert
// to values outside that range. Which means if the upper 4 bits of a resulting integer are non-zero then the corresponding character was invalid.
if (BOOST_LIKELY(_mm_test_all_zeros(_mm_or_si128(mm_lo, mm_hi), constants::mm_F0)))
{
if (BOOST_LIKELY(ec == from_chars_error::none))
{
__m128i mm = _mm_or_si128(mm_lo, _mm_slli_epi32(mm_hi, 4));
_mm_storeu_si128(reinterpret_cast< __m128i* >(data), mm);
}
}
else
{
// Some of the hex digits are invalid
const __m128i mm_0 = _mm_setzero_si128();
__m128i mm_hi_bits_lo = _mm_and_si128(mm_lo, constants::mm_F0);
__m128i mm_hi_bits_hi = _mm_and_si128(mm_hi, constants::mm_F0);
mm_hi_bits_lo = _mm_cmpeq_epi8(mm_hi_bits_lo, mm_0);
mm_hi_bits_hi = _mm_cmpeq_epi8(mm_hi_bits_hi, mm_0);
std::uint32_t digits_mask_lo = _mm_movemask_epi8(mm_hi_bits_lo);
std::uint32_t digits_mask_hi = _mm_movemask_epi8(mm_hi_bits_hi);
unsigned int pos_lo = detail::countr_zero_nz(~digits_mask_lo) * 2u + 1u;
unsigned int pos_hi = detail::countr_zero_nz(~digits_mask_hi) * 2u;
unsigned int pos = pos_lo < pos_hi ? pos_lo : pos_hi;
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;
}
}
}
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 output character type for from_chars");
using char_constants = uuids::detail::from_chars_simd_char_constants< Char >;
unsigned int end_pos = 36u;
from_chars_error ec = from_chars_error::none;
__m128i mm_chars1, mm_chars2, mm_chars3;
if (BOOST_LIKELY((end - begin) >= 36))
{
mm_chars1 = from_chars_simd_load_traits< Char >::load_packed_16(begin);
mm_chars2 = from_chars_simd_load_traits< Char >::load_packed_16(begin + 16);
mm_chars3 = from_chars_simd_load_traits< Char >::load_packed_4(begin + 32);
}
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)
{
mm_chars1 = from_chars_simd_load_traits< Char >::load_packed_16(p);
p += 16;
n -= 16u;
}
else
{
mm_chars1 = from_chars_simd_load_traits< Char >::load_packed_n(p, n);
p += n;
n = 0u;
}
if (n >= 16u)
{
mm_chars2 = from_chars_simd_load_traits< Char >::load_packed_16(p);
p += 16;
n -= 16u;
}
else
{
mm_chars2 = from_chars_simd_load_traits< Char >::load_packed_n(p, n);
p += n;
n = 0u;
}
mm_chars3 = from_chars_simd_load_traits< Char >::load_packed_n(p, n);
}
from_chars_simd_core
(
mm_chars1, mm_chars2, mm_chars3,
char_constants::mm_expected_dashes,
char_constants::mm_char_code1_cmp,
char_constants::mm_char_code2_cmp,
#if defined(BOOST_UUID_USE_AVX512_V1) || !defined(BOOST_UUID_USE_AVX)
char_constants::mm_char_code0_sub,
char_constants::mm_char_code1_sub,
char_constants::mm_char_code2_sub,
#else
char_constants::char_code_sub,
#endif
u.data(), end_pos, ec
);
return { begin + end_pos, ec };
}
} // namespace detail
} // namespace uuids
} // namespace boost
#endif // defined(BOOST_UUID_USE_SSE41)
#endif // BOOST_UUID_DETAIL_FROM_CHARS_X86_HPP_INCLUDED

View File

@@ -0,0 +1,49 @@
#ifndef BOOST_UUID_DETAIL_SIMD_VECTOR_HPP_INCLUDED
#define BOOST_UUID_DETAIL_SIMD_VECTOR_HPP_INCLUDED
// Copyright 2025 Andrey Semashev
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <type_traits>
#include <boost/config.hpp>
namespace boost {
namespace uuids {
namespace detail {
//! A helper to define vector constants
template< typename T, unsigned int ByteSize >
union simd_vector
{
static_assert(((ByteSize - 1u) & ByteSize) == 0u, "Boost.UUID: ByteSize must be a power of two in simd_vector");
BOOST_MAY_ALIAS T elements[ByteSize / sizeof(T)];
alignas(ByteSize) unsigned char bytes[ByteSize];
template<
typename Vector,
typename = typename std::enable_if< sizeof(Vector) <= ByteSize >::type
>
BOOST_FORCEINLINE operator Vector () const noexcept { return get< Vector >(); }
template< typename Vector >
BOOST_FORCEINLINE typename std::enable_if< sizeof(Vector) <= ByteSize, Vector >::type get() const noexcept
{
using vector_type = typename std::remove_cv< typename std::remove_reference< Vector >::type >::type;
return *reinterpret_cast< const vector_type* >(bytes);
}
};
template< typename T >
using simd_vector128 = simd_vector< T, 16u >;
template< typename T >
using simd_vector256 = simd_vector< T, 32u >;
template< typename T >
using simd_vector512 = simd_vector< T, 64u >;
} // namespace detail
} // namespace uuids
} // namespace boost
#endif // BOOST_UUID_DETAIL_SIMD_VECTOR_HPP_INCLUDED

View File

@@ -5,7 +5,7 @@
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/uuid/detail/from_chars.hpp>
#include <boost/uuid/detail/from_chars_result.hpp>
#include <boost/throw_exception.hpp>
#include <boost/config.hpp>
#include <stdexcept>

View File

@@ -11,12 +11,14 @@
#include <cstdint>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/detail/endian.hpp>
#include <boost/uuid/detail/simd_vector.hpp>
#if defined(BOOST_UUID_REPORT_IMPLEMENTATION)
#include <boost/config/pragma_message.hpp>
#if defined(BOOST_UUID_USE_AVX10_1)
BOOST_PRAGMA_MESSAGE( "Using to_chars_x86.hpp, AVX10.1" )
#if defined(BOOST_UUID_USE_AVX512_V1)
BOOST_PRAGMA_MESSAGE( "Using to_chars_x86.hpp, AVX512v1" )
#elif defined(BOOST_UUID_USE_AVX2)
BOOST_PRAGMA_MESSAGE( "Using to_chars_x86.hpp, AVX2" )
@@ -49,16 +51,16 @@ template<
>
struct to_chars_simd_char_constants
{
alignas(16) static const std::uint8_t mm_char_table[16];
alignas(16) static const std::uint8_t mm_char_dash[16];
static const simd_vector128< std::uint8_t > mm_char_table;
static const simd_vector128< std::uint8_t > mm_char_dash;
};
template< typename Char, bool IsCharASCIICompatible, bool IsWCharASCIICompatible >
alignas(16) const std::uint8_t to_chars_simd_char_constants< Char, IsCharASCIICompatible, IsWCharASCIICompatible >::mm_char_table[16] =
{ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 }; // 0123456789abcdef in ASCII
const simd_vector128< std::uint8_t > to_chars_simd_char_constants< Char, IsCharASCIICompatible, IsWCharASCIICompatible >::mm_char_table =
{{ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 }}; // 0123456789abcdef in ASCII
template< typename Char, bool IsCharASCIICompatible, bool IsWCharASCIICompatible >
alignas(16) const std::uint8_t to_chars_simd_char_constants< Char, IsCharASCIICompatible, IsWCharASCIICompatible >::mm_char_dash[16] =
{ 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D }; // ---------------- in ASCII
const simd_vector128< std::uint8_t > to_chars_simd_char_constants< Char, IsCharASCIICompatible, IsWCharASCIICompatible >::mm_char_dash =
{{ 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D }}; // ---------------- in ASCII
template< bool IsWCharASCIICompatible >
struct to_chars_simd_char_constants< char, false, IsWCharASCIICompatible >
@@ -67,26 +69,26 @@ struct to_chars_simd_char_constants< char, false, IsWCharASCIICompatible >
static_assert(static_cast< std::uint8_t >('-') < static_cast< std::uint8_t >('0') && static_cast< std::uint8_t >('-') < static_cast< std::uint8_t >('a'),
"Boost.UUID: Unsupported char encoding, '-' character code is expected to be less than any hexadecimal characters");
alignas(16) static const std::uint8_t mm_char_table[16];
alignas(16) static const std::uint8_t mm_char_dash[16];
static const simd_vector128< std::uint8_t > mm_char_table;
static const simd_vector128< std::uint8_t > mm_char_dash;
};
template< bool IsWCharASCIICompatible >
alignas(16) const std::uint8_t to_chars_simd_char_constants< char, false, IsWCharASCIICompatible >::mm_char_table[16] =
{
const simd_vector128< std::uint8_t > to_chars_simd_char_constants< char, false, IsWCharASCIICompatible >::mm_char_table =
{{
static_cast< std::uint8_t >('0'), static_cast< std::uint8_t >('1'), static_cast< std::uint8_t >('2'), static_cast< std::uint8_t >('3'),
static_cast< std::uint8_t >('4'), static_cast< std::uint8_t >('5'), static_cast< std::uint8_t >('6'), static_cast< std::uint8_t >('7'),
static_cast< std::uint8_t >('8'), static_cast< std::uint8_t >('9'), static_cast< std::uint8_t >('a'), static_cast< std::uint8_t >('b'),
static_cast< std::uint8_t >('c'), static_cast< std::uint8_t >('d'), static_cast< std::uint8_t >('e'), static_cast< std::uint8_t >('f')
};
}};
template< bool IsWCharASCIICompatible >
alignas(16) const std::uint8_t to_chars_simd_char_constants< char, false, IsWCharASCIICompatible >::mm_char_dash[16] =
{
const simd_vector128< std::uint8_t > to_chars_simd_char_constants< char, false, IsWCharASCIICompatible >::mm_char_dash =
{{
static_cast< std::uint8_t >('-'), static_cast< std::uint8_t >('-'), static_cast< std::uint8_t >('-'), static_cast< std::uint8_t >('-'),
static_cast< std::uint8_t >('-'), static_cast< std::uint8_t >('-'), static_cast< std::uint8_t >('-'), static_cast< std::uint8_t >('-'),
static_cast< std::uint8_t >('-'), static_cast< std::uint8_t >('-'), static_cast< std::uint8_t >('-'), static_cast< std::uint8_t >('-'),
static_cast< std::uint8_t >('-'), static_cast< std::uint8_t >('-'), static_cast< std::uint8_t >('-'), static_cast< std::uint8_t >('-')
};
}};
template< bool IsCharASCIICompatible >
struct to_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false >
@@ -100,44 +102,44 @@ struct to_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false >
static_assert(static_cast< std::uint8_t >(L'-') < static_cast< std::uint8_t >(L'0') && static_cast< std::uint8_t >(L'-') < static_cast< std::uint8_t >(L'a'),
"Boost.UUID: Unsupported wchar_t encoding, L'-' character code is expected to be less than any hexadecimal characters");
alignas(16) static const std::uint8_t mm_char_table[16];
alignas(16) static const std::uint8_t mm_char_dash[16];
static const simd_vector128< std::uint8_t > mm_char_table;
static const simd_vector128< std::uint8_t > mm_char_dash;
};
template< bool IsCharASCIICompatible >
alignas(16) const std::uint8_t to_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false >::mm_char_table[16] =
{
const simd_vector128< std::uint8_t > to_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false >::mm_char_table =
{{
static_cast< std::uint8_t >(L'0'), static_cast< std::uint8_t >(L'1'), static_cast< std::uint8_t >(L'2'), static_cast< std::uint8_t >(L'3'),
static_cast< std::uint8_t >(L'4'), static_cast< std::uint8_t >(L'5'), static_cast< std::uint8_t >(L'6'), static_cast< std::uint8_t >(L'7'),
static_cast< std::uint8_t >(L'8'), static_cast< std::uint8_t >(L'9'), static_cast< std::uint8_t >(L'a'), static_cast< std::uint8_t >(L'b'),
static_cast< std::uint8_t >(L'c'), static_cast< std::uint8_t >(L'd'), static_cast< std::uint8_t >(L'e'), static_cast< std::uint8_t >(L'f')
};
}};
template< bool IsCharASCIICompatible >
alignas(16) const std::uint8_t to_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false >::mm_char_dash[16] =
{
const simd_vector128< std::uint8_t > to_chars_simd_char_constants< wchar_t, IsCharASCIICompatible, false >::mm_char_dash =
{{
static_cast< std::uint8_t >(L'-'), static_cast< std::uint8_t >(L'-'), static_cast< std::uint8_t >(L'-'), static_cast< std::uint8_t >(L'-'),
static_cast< std::uint8_t >(L'-'), static_cast< std::uint8_t >(L'-'), static_cast< std::uint8_t >(L'-'), static_cast< std::uint8_t >(L'-'),
static_cast< std::uint8_t >(L'-'), static_cast< std::uint8_t >(L'-'), static_cast< std::uint8_t >(L'-'), static_cast< std::uint8_t >(L'-'),
static_cast< std::uint8_t >(L'-'), static_cast< std::uint8_t >(L'-'), static_cast< std::uint8_t >(L'-'), static_cast< std::uint8_t >(L'-')
};
}};
template< typename >
struct to_chars_simd_constants
{
alignas(16) static const std::uint8_t mm_15[16];
alignas(16) static const std::uint8_t mm_shuffle_pattern1[16];
alignas(16) static const std::uint8_t mm_shuffle_pattern2[16];
static const simd_vector128< std::uint8_t > mm_0F;
static const simd_vector128< std::uint8_t > mm_shuffle_pattern1;
static const simd_vector128< std::uint8_t > mm_shuffle_pattern2;
};
template< typename T >
alignas(16) const std::uint8_t to_chars_simd_constants< T >::mm_15[16] =
{ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F };
const simd_vector128< std::uint8_t > to_chars_simd_constants< T >::mm_0F =
{{ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F }};
template< typename T >
alignas(16) const std::uint8_t to_chars_simd_constants< T >::mm_shuffle_pattern1[16] =
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x80, 0x08, 0x09, 0x0A, 0x0B, 0x80, 0x0C, 0x0D };
const simd_vector128< std::uint8_t > to_chars_simd_constants< T >::mm_shuffle_pattern1 =
{{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x80, 0x08, 0x09, 0x0A, 0x0B, 0x80, 0x0C, 0x0D }};
template< typename T >
alignas(16) const std::uint8_t to_chars_simd_constants< T >::mm_shuffle_pattern2[16] =
{ 0x00, 0x01, 0x80, 0x02, 0x03, 0x04, 0x05, 0x80, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D };
const simd_vector128< std::uint8_t > to_chars_simd_constants< T >::mm_shuffle_pattern2 =
{{ 0x00, 0x01, 0x80, 0x02, 0x03, 0x04, 0x05, 0x80, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D }};
//! Converts UUID to a string of 36 characters, where first 32 craracters are returned in mm_chars1 and mm_chars2 and the last 4 in the highest 32 bits of mm_chars3
BOOST_FORCEINLINE void to_chars_simd_core
@@ -147,15 +149,14 @@ BOOST_FORCEINLINE void to_chars_simd_core
__m128i& mm_chars1, __m128i& mm_chars2, __m128i& mm_chars3
) noexcept
{
__m128i const& mm_15 = *reinterpret_cast< const __m128i* >(uuids::detail::to_chars_simd_constants< void >::mm_15);
__m128i const& mm_shuffle_pattern1 = *reinterpret_cast< const __m128i* >(uuids::detail::to_chars_simd_constants< void >::mm_shuffle_pattern1);
__m128i const& mm_shuffle_pattern2 = *reinterpret_cast< const __m128i* >(uuids::detail::to_chars_simd_constants< void >::mm_shuffle_pattern2);
using constants = uuids::detail::to_chars_simd_constants< void >;
__m128i mm_input = uuids::detail::load_unaligned_si128(data);
__m128i mm_input = _mm_loadu_si128(reinterpret_cast< const __m128i* >(data));
// Split half-bytes
__m128i mm_input_hi = _mm_and_si128(_mm_srli_epi32(mm_input, 4), mm_15);
__m128i mm_input_lo = _mm_and_si128(mm_input, mm_15);
__m128i const& mm_0F = constants::mm_0F;
__m128i mm_input_hi = _mm_and_si128(_mm_srli_epi32(mm_input, 4), mm_0F);
__m128i mm_input_lo = _mm_and_si128(mm_input, mm_0F);
// Stringize each of the halves
mm_input_hi = _mm_shuffle_epi8(mm_char_table, mm_input_hi);
@@ -171,23 +172,31 @@ BOOST_FORCEINLINE void to_chars_simd_core
// |01234567-89ab-cd|ef-0123-456789ab|
//
// Note that the last "cdef" characters are already available at the end of mm_2
mm_chars1 = _mm_shuffle_epi8(mm_1, mm_shuffle_pattern1);
mm_chars2 = _mm_shuffle_epi8(_mm_alignr_epi8(mm_2, mm_1, 14), mm_shuffle_pattern2);
mm_chars1 = _mm_shuffle_epi8(mm_1, constants::mm_shuffle_pattern1);
mm_chars2 = _mm_shuffle_epi8(_mm_alignr_epi8(mm_2, mm_1, 14), constants::mm_shuffle_pattern2);
mm_chars1 = _mm_max_epu8(mm_chars1, mm_char_dash);
mm_chars2 = _mm_max_epu8(mm_chars2, mm_char_dash);
mm_chars3 = mm_2;
}
#if defined(BOOST_MSVC)
#pragma warning(push)
// conditional expression is constant
#pragma warning(disable: 4127)
#endif
template< typename Char >
BOOST_FORCEINLINE Char* to_chars_simd(uuid const& u, Char* out) noexcept
{
using char_constants = uuids::detail::to_chars_simd_char_constants< Char >;
__m128i mm_chars1, mm_chars2, mm_chars3;
uuids::detail::to_chars_simd_core
(
u.data,
*reinterpret_cast< const __m128i* >(uuids::detail::to_chars_simd_char_constants< Char >::mm_char_table),
*reinterpret_cast< const __m128i* >(uuids::detail::to_chars_simd_char_constants< Char >::mm_char_dash),
u.data(),
char_constants::mm_char_table,
char_constants::mm_char_dash,
mm_chars1, mm_chars2, mm_chars3
);
@@ -197,9 +206,9 @@ BOOST_FORCEINLINE Char* to_chars_simd(uuid const& u, Char* out) noexcept
_mm_storeu_si128(reinterpret_cast< __m128i* >(out), mm_chars1);
_mm_storeu_si128(reinterpret_cast< __m128i* >(out + 16), mm_chars2);
#if defined(BOOST_UUID_USE_SSE41)
*reinterpret_cast< BOOST_MAY_ALIAS std::uint32_t* >(out + 32) = _mm_extract_epi32(mm_chars3, 3);
detail::store_native_u32(out + 32, _mm_extract_epi32(mm_chars3, 3));
#else
*reinterpret_cast< BOOST_MAY_ALIAS std::uint32_t* >(out + 32) = _mm_cvtsi128_si32(_mm_srli_si128(mm_chars3, 12));
detail::store_native_u32(out + 32, _mm_cvtsi128_si32(_mm_srli_si128(mm_chars3, 12)));
#endif
}
else BOOST_IF_CONSTEXPR (sizeof(Char) == 2u)
@@ -215,7 +224,7 @@ BOOST_FORCEINLINE Char* to_chars_simd(uuid const& u, Char* out) noexcept
_mm_storeu_si128(reinterpret_cast< __m128i* >(out + 24), _mm_unpackhi_epi8(mm_chars2, mm_0));
#endif
#if defined(BOOST_UUID_USE_SSE41) && (defined(__x86_64__) || defined(_M_X64))
*reinterpret_cast< BOOST_MAY_ALIAS std::uint64_t* >(out + 32) = _mm_extract_epi64(_mm_unpackhi_epi8(mm_chars3, mm_0), 1);
detail::store_native_u64(out + 32, _mm_extract_epi64(_mm_unpackhi_epi8(mm_chars3, mm_0), 1));
#else
_mm_storeh_pd(reinterpret_cast< BOOST_MAY_ALIAS double* >(out + 32), _mm_castsi128_pd(_mm_unpackhi_epi8(mm_chars3, mm_0)));
#endif
@@ -223,7 +232,7 @@ BOOST_FORCEINLINE Char* to_chars_simd(uuid const& u, Char* out) noexcept
else
{
const __m128i mm_0 = _mm_setzero_si128();
#if 0 && defined(BOOST_UUID_USE_AVX10_1)
#if defined(BOOST_UUID_USE_AVX512_V1) && defined(BOOST_UUID_TO_FROM_CHARS_X86_USE_ZMM)
// Slower than the AVX2 version below on Intel Golden Cove. Perhaps, it will become beneficial on newer microarchitectures.
_mm512_storeu_epi32(out, _mm512_cvtepu8_epi32(mm_chars1));
_mm512_storeu_epi32(out + 16, _mm512_cvtepu8_epi32(mm_chars2));
@@ -252,6 +261,10 @@ BOOST_FORCEINLINE Char* to_chars_simd(uuid const& u, Char* out) noexcept
return out + 36;
}
#if defined(BOOST_MSVC)
#pragma warning(pop)
#endif
} // namespace detail
} // namespace uuids
} // namespace boost

View File

@@ -22,8 +22,8 @@
#if defined(BOOST_UUID_REPORT_IMPLEMENTATION)
#include <boost/config/pragma_message.hpp>
#if defined(BOOST_UUID_USE_AVX10_1)
BOOST_PRAGMA_MESSAGE( "Using uuid_x86.ipp, AVX10.1" )
#if defined(BOOST_UUID_USE_AVX512_V1)
BOOST_PRAGMA_MESSAGE( "Using uuid_x86.ipp, AVX512v1" )
#elif defined(BOOST_UUID_USE_SSE41)
BOOST_PRAGMA_MESSAGE( "Using uuid_x86.ipp, SSE4.1" )
@@ -38,7 +38,7 @@ BOOST_PRAGMA_MESSAGE( "Using uuid_x86.ipp, SSE2" )
#endif // #if defined(BOOST_UUID_REPORT_IMPLEMENTATION)
// MSVC does not always have immintrin.h (at least, not up to MSVC 10), so include the appropriate header for each instruction set
#if defined(BOOST_UUID_USE_AVX10_1)
#if defined(BOOST_UUID_USE_AVX512_V1)
#include <immintrin.h>
#elif defined(BOOST_UUID_USE_SSE41)
#include <smmintrin.h>
@@ -52,15 +52,10 @@ namespace boost {
namespace uuids {
namespace detail {
BOOST_FORCEINLINE __m128i load_unaligned_si128(const std::uint8_t* p) noexcept
{
return _mm_loadu_si128(reinterpret_cast< const __m128i* >(p));
}
BOOST_FORCEINLINE void compare(uuid const& lhs, uuid const& rhs, std::uint32_t& cmp, std::uint32_t& rcmp) noexcept
{
__m128i mm_left = uuids::detail::load_unaligned_si128(lhs.data);
__m128i mm_right = uuids::detail::load_unaligned_si128(rhs.data);
__m128i mm_left = _mm_loadu_si128(reinterpret_cast< const __m128i* >(lhs.data()));
__m128i mm_right = _mm_loadu_si128(reinterpret_cast< const __m128i* >(rhs.data()));
// To emulate lexicographical_compare behavior we have to perform two comparisons - the forward and reverse one.
// Then we know which bytes are equivalent and which ones are different, and for those different the comparison results
@@ -77,7 +72,7 @@ BOOST_FORCEINLINE void compare(uuid const& lhs, uuid const& rhs, std::uint32_t&
// with another XOR to the comparison results.
// 3. Until AVX-512, there is only pcmpgtb instruction that compares for "greater" relation, so we swap the arguments to get what we need.
#if defined(BOOST_UUID_USE_AVX10_1)
#if defined(BOOST_UUID_USE_AVX512_V1)
__mmask16 k_cmp = _mm_cmplt_epu8_mask(mm_left, mm_right);
__mmask16 k_rcmp = _mm_cmplt_epu8_mask(mm_right, mm_left);
@@ -85,7 +80,7 @@ BOOST_FORCEINLINE void compare(uuid const& lhs, uuid const& rhs, std::uint32_t&
cmp = static_cast< std::uint32_t >(_cvtmask16_u32(k_cmp));
rcmp = static_cast< std::uint32_t >(_cvtmask16_u32(k_rcmp));
#else // defined(BOOST_UUID_USE_AVX10_1)
#else // defined(BOOST_UUID_USE_AVX512_V1)
const __m128i mm_signs_mask = _mm_xor_si128(mm_left, mm_right);
@@ -97,7 +92,7 @@ BOOST_FORCEINLINE void compare(uuid const& lhs, uuid const& rhs, std::uint32_t&
cmp = static_cast< std::uint32_t >(_mm_movemask_epi8(mm_cmp));
rcmp = static_cast< std::uint32_t >(_mm_movemask_epi8(mm_rcmp));
#endif // defined(BOOST_UUID_USE_AVX10_1)
#endif // defined(BOOST_UUID_USE_AVX512_V1)
cmp = (cmp - 1u) ^ cmp;
rcmp = (rcmp - 1u) ^ rcmp;
@@ -113,8 +108,8 @@ BOOST_UUID_CXX14_CONSTEXPR_RT inline bool operator== (uuid const& lhs, uuid cons
}
else
{
__m128i mm_left = uuids::detail::load_unaligned_si128(lhs.data);
__m128i mm_right = uuids::detail::load_unaligned_si128(rhs.data);
__m128i mm_left = _mm_loadu_si128(reinterpret_cast< const __m128i* >(lhs.data()));
__m128i mm_right = _mm_loadu_si128(reinterpret_cast< const __m128i* >(rhs.data()));
#if defined(BOOST_UUID_USE_SSE41)
__m128i mm = _mm_xor_si128(mm_left, mm_right);

View File

@@ -33,11 +33,17 @@ boost_test(TYPE run SOURCES test_io.cpp LINK_LIBRARIES Boost::lexical_cast Boost
boost_test(TYPE run SOURCES test_io.cpp LINK_LIBRARIES Boost::lexical_cast Boost::predef COMPILE_DEFINITIONS BOOST_UUID_NO_SIMD=1 BOOST_UUID_REPORT_IMPLEMENTATION=1 NAME test_io_no_simd)
boost_test(TYPE run SOURCES test_io_2.cpp COMPILE_DEFINITIONS BOOST_UUID_REPORT_IMPLEMENTATION=1)
boost_test(TYPE run SOURCES test_io_2.cpp COMPILE_DEFINITIONS BOOST_UUID_NO_SIMD=1 BOOST_UUID_REPORT_IMPLEMENTATION=1 NAME test_io_2_no_simd)
boost_test(TYPE run SOURCES test_to_chars.cpp COMPILE_DEFINITIONS BOOST_UUID_REPORT_IMPLEMENTATION=1)
boost_test(TYPE run SOURCES test_to_chars.cpp COMPILE_DEFINITIONS BOOST_UUID_NO_SIMD=1 BOOST_UUID_REPORT_IMPLEMENTATION=1 NAME test_to_chars_no_simd)
boost_test(TYPE run SOURCES test_to_chars_2.cpp COMPILE_DEFINITIONS BOOST_UUID_REPORT_IMPLEMENTATION=1)
boost_test(TYPE run SOURCES test_to_chars_2.cpp COMPILE_DEFINITIONS BOOST_UUID_NO_SIMD=1 BOOST_UUID_REPORT_IMPLEMENTATION=1 NAME test_to_chars_2_no_simd)
boost_test(TYPE run SOURCES test_from_chars.cpp COMPILE_DEFINITIONS BOOST_UUID_REPORT_IMPLEMENTATION=1)
boost_test(TYPE run SOURCES test_from_chars.cpp COMPILE_DEFINITIONS BOOST_UUID_NO_SIMD=1 BOOST_UUID_REPORT_IMPLEMENTATION=1 NAME test_from_chars_no_simd)
boost_test(TYPE run SOURCES test_from_chars_2.cpp COMPILE_DEFINITIONS BOOST_UUID_REPORT_IMPLEMENTATION=1)
boost_test(TYPE run SOURCES test_from_chars_2.cpp COMPILE_DEFINITIONS BOOST_UUID_NO_SIMD=1 BOOST_UUID_REPORT_IMPLEMENTATION=1 NAME test_from_chars_2_no_simd)
boost_test(TYPE run SOURCES test_uuid_clock.cpp)
boost_test(TYPE run SOURCES test_nil_generator.cpp)
@@ -91,4 +97,9 @@ boost_test(TYPE compile SOURCES test_uuid_cx.cpp)
boost_test(TYPE run SOURCES test_string_generator_cx.cpp)
boost_test(TYPE run SOURCES test_constants_cx.cpp)
boost_test(TYPE run SOURCES test_to_chars_cx.cpp LINK_LIBRARIES Boost::array)
boost_test(TYPE run SOURCES test_from_chars_cx.cpp)
boost_test(TYPE run SOURCES test_from_chars_cx2.cpp)
boost_test(TYPE run SOURCES quick.cpp)

View File

@@ -100,8 +100,10 @@ run test_to_chars.cpp : : : <define>BOOST_UUID_NO_SIMD <define>BOOST_UUID_REPORT
run test_to_chars_2.cpp : : : <define>BOOST_UUID_REPORT_IMPLEMENTATION ;
run test_to_chars_2.cpp : : : <define>BOOST_UUID_NO_SIMD <define>BOOST_UUID_REPORT_IMPLEMENTATION : test_to_chars_2_no_simd ;
run test_from_chars.cpp ;
run test_from_chars_2.cpp ;
run test_from_chars.cpp : : : <define>BOOST_UUID_REPORT_IMPLEMENTATION ;
run test_from_chars.cpp : : : <define>BOOST_UUID_NO_SIMD <define>BOOST_UUID_REPORT_IMPLEMENTATION : test_from_chars_no_simd ;
run test_from_chars_2.cpp : : : <define>BOOST_UUID_REPORT_IMPLEMENTATION ;
run test_from_chars_2.cpp : : : <define>BOOST_UUID_NO_SIMD <define>BOOST_UUID_REPORT_IMPLEMENTATION : test_from_chars_2_no_simd ;
# test uuid_clock

View File

@@ -33,6 +33,30 @@ int main()
test( U"", 0, from_chars_error::unexpected_end_of_input );
test( u8"", 0, from_chars_error::unexpected_end_of_input );
test( "0", 1, from_chars_error::unexpected_end_of_input );
test( L"0", 1, from_chars_error::unexpected_end_of_input );
test( u"0", 1, from_chars_error::unexpected_end_of_input );
test( U"0", 1, from_chars_error::unexpected_end_of_input );
test( u8"0", 1, from_chars_error::unexpected_end_of_input );
test( "01", 2, from_chars_error::unexpected_end_of_input );
test( L"01", 2, from_chars_error::unexpected_end_of_input );
test( u"01", 2, from_chars_error::unexpected_end_of_input );
test( U"01", 2, from_chars_error::unexpected_end_of_input );
test( u8"01", 2, from_chars_error::unexpected_end_of_input );
test( "01234567-89aB-cDeF-0123-456789AbCd", 34, from_chars_error::unexpected_end_of_input );
test( L"01234567-89aB-cDeF-0123-456789AbCd", 34, from_chars_error::unexpected_end_of_input );
test( u"01234567-89aB-cDeF-0123-456789AbCd", 34, from_chars_error::unexpected_end_of_input );
test( U"01234567-89aB-cDeF-0123-456789AbCd", 34, from_chars_error::unexpected_end_of_input );
test( u8"01234567-89aB-cDeF-0123-456789AbCd", 34, from_chars_error::unexpected_end_of_input );
test( "01234567-89aB-cDeF-0123-456789AbCdE", 35, from_chars_error::unexpected_end_of_input );
test( L"01234567-89aB-cDeF-0123-456789AbCdE", 35, from_chars_error::unexpected_end_of_input );
test( u"01234567-89aB-cDeF-0123-456789AbCdE", 35, from_chars_error::unexpected_end_of_input );
test( U"01234567-89aB-cDeF-0123-456789AbCdE", 35, from_chars_error::unexpected_end_of_input );
test( u8"01234567-89aB-cDeF-0123-456789AbCdE", 35, from_chars_error::unexpected_end_of_input );
test( "@", 0, from_chars_error::hex_digit_expected );
test( L"@", 0, from_chars_error::hex_digit_expected );
test( u"@", 0, from_chars_error::hex_digit_expected );

View File

@@ -11,7 +11,7 @@
using namespace boost::uuids;
template<class Ch, std::size_t N>
BOOST_CXX14_CONSTEXPR uuid uuid_from_string( Ch const (&str)[ N ] )
BOOST_UUID_CXX14_CONSTEXPR_RT uuid uuid_from_string( Ch const (&str)[ N ] )
{
Ch const* first = str;
Ch const* last = first + N - 1;
@@ -22,7 +22,7 @@ BOOST_CXX14_CONSTEXPR uuid uuid_from_string( Ch const (&str)[ N ] )
return u;
}
#define TEST(str) { BOOST_CXX14_CONSTEXPR auto u = uuid_from_string(str); BOOST_TEST_EQ(u, expected); }
#define TEST(str) { BOOST_UUID_CXX14_CONSTEXPR_RT auto u = uuid_from_string(str); BOOST_TEST_EQ(u, expected); }
int main()
{

View File

@@ -17,7 +17,7 @@ struct test_result
};
template<class Ch, std::size_t N>
BOOST_CXX14_CONSTEXPR test_result test( Ch const (&str)[ N ] )
BOOST_UUID_CXX14_CONSTEXPR_RT test_result test( Ch const (&str)[ N ] )
{
Ch const* first = str;
Ch const* last = first + N - 1;
@@ -28,7 +28,7 @@ BOOST_CXX14_CONSTEXPR test_result test( Ch const (&str)[ N ] )
return { r.ptr - first, r.ec };
}
#define TEST(Str, Pos, Ec) { BOOST_CXX14_CONSTEXPR auto r = test(Str); BOOST_TEST_EQ(Pos, r.pos); BOOST_TEST_EQ(static_cast<int>(Ec), static_cast<int>(r.ec)); }
#define TEST(Str, Pos, Ec) { BOOST_UUID_CXX14_CONSTEXPR_RT auto r = test(Str); BOOST_TEST_EQ(Pos, r.pos); BOOST_TEST_EQ(static_cast<int>(Ec), static_cast<int>(r.ec)); }
int main()
{