├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── Config.cmake.in ├── LICENSE ├── README.md ├── benchmarks ├── CMakeLists.txt ├── basic.h ├── compare_benchmark.cpp └── uuid_v4_benchmark.cpp ├── endianness.h ├── example.cpp ├── tests ├── CMakeLists.txt └── uuid_v4_test.cpp └── uuid_v4.h /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/google/googletest"] 2 | path = vendor/google/googletest 3 | url = https://github.com/google/googletest.git 4 | [submodule "vendor/google/benchmark"] 5 | path = vendor/google/benchmark 6 | url = https://github.com/google/benchmark.git 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.8) 2 | 3 | if (NOT DEFINED CMAKE_BUILD_TYPE) 4 | set(CMAKE_BUILD_TYPE Release) 5 | endif () 6 | 7 | project (uuid_v4 VERSION 1.0.0 LANGUAGES CXX) 8 | 9 | option(test "Build all tests." OFF) 10 | option(test_use_internal_googletest "Build googletest, do not use it from system." ON) 11 | option(benchmark "Build benchmarks." OFF) 12 | option(benchmark_use_internal_googlebenchmark "Build google benchmark, do not use it from system." ON) 13 | 14 | set(CMAKE_CXX_STANDARD 17) 15 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 16 | set(CMAKE_CXX_EXTENSIONS OFF) 17 | 18 | if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 19 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native") 20 | elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 21 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 22 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") 23 | endif () 24 | 25 | if (test) 26 | if (test_use_internal_googletest) 27 | # build tests (targets: gtest_main, gtest) 28 | add_subdirectory(vendor/google/googletest/googletest) 29 | if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 30 | target_compile_options (gtest PRIVATE "-Wno-error=misleading-indentation") 31 | endif() 32 | 33 | add_library(GTest::gtest INTERFACE IMPORTED) 34 | target_link_libraries(GTest::gtest INTERFACE gtest) 35 | add_library(GTest::gtest_main INTERFACE IMPORTED) 36 | target_link_libraries(GTest::gtest_main INTERFACE gtest_main) 37 | else () 38 | find_package(GTest REQUIRED) 39 | endif () 40 | 41 | enable_testing () 42 | add_subdirectory (tests) 43 | endif () 44 | 45 | if (benchmark) 46 | if (benchmark_use_internal_googlebenchmark) 47 | # build google benchmark (target: benchmark) 48 | # do not build tests of benchmarking lib 49 | set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Suppressing benchmark's tests" FORCE) 50 | add_subdirectory(vendor/google/benchmark) 51 | add_library(benchmark::benchmark INTERFACE IMPORTED) 52 | target_link_libraries(benchmark::benchmark INTERFACE benchmark) 53 | else () 54 | find_package(benchmark REQUIRED) 55 | endif () 56 | 57 | add_subdirectory (benchmarks) 58 | endif () 59 | 60 | add_executable(example example.cpp) 61 | target_link_libraries (example) 62 | 63 | add_library(${PROJECT_NAME} INTERFACE) 64 | add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 65 | 66 | if (NOT CMAKE_VERSION VERSION_LESS 3.8) 67 | target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_17) 68 | endif() 69 | 70 | target_include_directories(${PROJECT_NAME} INTERFACE $) 71 | 72 | ################################################################################ 73 | ## PACKAGE SUPPORT 74 | ################################################################################ 75 | 76 | set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") 77 | 78 | set(include_install_dir "include") 79 | set(config_install_dir "lib/cmake/${PROJECT_NAME}") 80 | 81 | 82 | set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") 83 | set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") 84 | set(targets_export_name "${PROJECT_NAME}Targets") 85 | set(namespace "${PROJECT_NAME}::") 86 | 87 | include(CMakePackageConfigHelpers) 88 | 89 | # CMake automatically adds an architecture compatibility check to make sure 90 | # 32 and 64 bit code is not accidentally mixed. For a header-only library this 91 | # is not required. The check can be disabled by temporarily unsetting 92 | # CMAKE_SIZEOF_VOID_P. In CMake 3.14 and later this can be achieved more cleanly 93 | # with write_basic_package_version_file(ARCH_INDEPENDENT). 94 | # TODO: Use this once a newer CMake can be required. 95 | set(TMP_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) 96 | unset(CMAKE_SIZEOF_VOID_P) 97 | write_basic_package_version_file( 98 | "${version_config}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion 99 | ) 100 | set(CMAKE_SIZEOF_VOID_P ${TMP_SIZEOF_VOID_P}) 101 | 102 | configure_file("Config.cmake.in" "${project_config}" @ONLY) 103 | 104 | install(TARGETS ${PROJECT_NAME} 105 | EXPORT "${targets_export_name}" 106 | INCLUDES DESTINATION "${include_install_dir}") 107 | 108 | install(FILES "uuid_v4.h" 109 | DESTINATION "${include_install_dir}/uuid_v4") 110 | install(FILES "endianness.h" 111 | DESTINATION "${include_install_dir}/uuid_v4") 112 | 113 | install(FILES "${project_config}" "${version_config}" 114 | DESTINATION "${config_install_dir}") 115 | 116 | install(EXPORT "${targets_export_name}" 117 | NAMESPACE "${namespace}" 118 | DESTINATION "${config_install_dir}") 119 | -------------------------------------------------------------------------------- /Config.cmake.in: -------------------------------------------------------------------------------- 1 | if(NOT TARGET @PROJECT_NAME@::@PROJECT_NAME@) 2 | # Provide path for scripts 3 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") 4 | 5 | include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake") 6 | endif() 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Xavier "Crashoz" Launey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uuid_v4 2 | 3 | This is a fast C++ header-only library to generate, serialize, print and parse UUIDs version 4 variant 1 as specified in [RFC-4122]. 4 | It heavily relies on SIMD operations (instruction sets **SSE4.1**/**AVX**/**AVX2**), c\++11 PRNG library and some c\++17 features. 5 | 6 | This library generates UUIDs with pseudo-random numbers, seeded by true (hardware) random. It is *not* a cryptographically secure way of generating UUIDs. 7 | 8 | While this lib is optimized to be fast with SIMD operations, it is possible to run it on any architecture with portable implementations of SIMD instructions like [simd-everywhere](https://github.com/simd-everywhere/simde) 9 | 10 | ## Update Notes 11 | 12 | The namespace changed from `UUID` to `UUIDv4` to avoid a conflict with a windows.h dependency. 13 | 14 | ## Usage 15 | 16 | ### Cmake 17 | 18 | ``` 19 | mkdir build && cd build 20 | cmake -DCMAKE_INSTALL_PREFIX="/usr/local" .. 21 | cmake --install . 22 | ``` 23 | 24 | Then use 25 | 26 | ``` 27 | find_package(uuid_v4) 28 | target_link_libraries(MyProject uuid_v4::uuid_v4) 29 | ``` 30 | 31 | ### Manually 32 | 33 | Include `"uuid_v4.h"` and `"endianness.h"`. 34 | 35 | ## Documentation 36 | 37 | To start generating UUIDs you need to create an object `UUIDv4::UUIDGenerator` where random_generator is a c\++11 Random number engine (see [random]). 38 | It is highly recommended to use the default engine `std::mt19937_64` as it has a SIMD implementation (at least in libstdc++) and provides better randomness. 39 | 40 | ```c++ 41 | #include "uuid_v4" 42 | UUIDv4::UUIDGenerator uuidGenerator; 43 | UUIDv4::UUID uuid = uuidGenerator.getUUID(); 44 | ``` 45 | 46 | Serializing an UUID to a byte string (16 bytes) 47 | ```c++ 48 | std::string bytes = uuid.bytes(); 49 | or 50 | std::string bytes1; 51 | uuid.bytes(bytes1); 52 | or 53 | char bytes2[16]; 54 | uuid.bytes(bytes2); 55 | ``` 56 | 57 | Pretty-printing an UUID (36 bytes) 58 | ```c++ 59 | std::string s = uuid.str(); 60 | or 61 | std::string s1; 62 | uuid.str(s1); 63 | or 64 | char s2[36]; 65 | uuid.bytes(s2); 66 | ``` 67 | 68 | Loading an UUID from a byte string (16 bytes) 69 | ```c++ 70 | UUIDv4::UUID uuid(bytes); 71 | ``` 72 | 73 | Parsing an UUID from a pretty string (36 bytes) 74 | ```c++ 75 | UUIDv4::UUID uuid = UUIDv4::UUID::fromStrFactory(string); 76 | or 77 | UUIDv4::UUID uuid; 78 | uuid.fromStr(string); 79 | ``` 80 | 81 | Comparing UUIDs 82 | ```c++ 83 | UUIDv4::UUIDGenerator uuidGenerator; 84 | UUIDv4::UUID uuid1 = uuidGenerator.getUUID(); 85 | UUIDv4::UUID uuid2 = uuidGenerator.getUUID(); 86 | if (uuid1 == uuid2) { 87 | std::cout << "1 in 10^36 chances of this printing" << std::endl 88 | } 89 | ``` 90 | 91 | stream operations 92 | ```c++ 93 | std::cout << uuid << std::endl; 94 | std::cin >> uuid; 95 | ``` 96 | ## Benchmarks 97 | 98 | Comparing generation time 99 | + Basic approach generating directly a string [basic] 100 | + libuuid [libuuid] uses /dev/urandom (cryptographically secure) 101 | + Boost UUID [boost] uses /dev/urandom (cryptographically secure) 102 | + Boost UUID with mt19937_64 103 | + UUID_v4 (this project) 104 | 105 | |Benchmark | Time | CPU |Iterations 106 | |------------------|---------------|------------|----------- 107 | |Basic | 16098 ns | 16021 ns | 42807 108 | |Libuuid | 298655 ns | 293749 ns | 2405 109 | |BoostUUID | 48476 ns | 48357 ns | 14689 110 | |BoostUUIDmt19937 | 2673 ns | 2665 ns | 262395 111 | |UUID_v4 | 1117 ns | 1114 ns | 618670 112 | 113 | 114 | Timings of UUIDs operations, there is a scale factor on x100. 115 | i.e UUIDGeneration takes 11.34ns to build one uuid. 116 | 117 | Benchmark | Time | CPU |Iterations 118 | -----------------------|-----------------|-----------|----------- 119 | UUIDGeneration | 1134 ns | 1117 ns | 618589 120 | UUIDSerializeAlloc | 3197 ns | 3182 ns | 214742 121 | UUIDSerializeByRef | 211 ns | 211 ns | 3312380 122 | UUIDSerializeCharArray | 64 ns | 64 ns | 10747617 123 | UUIDPretty | 3424 ns | 3415 ns | 206672 124 | UUIDPrettyByRef | 211 ns | 209 ns | 3319069 125 | UUIDPrettyCharArray | 88 ns | 88 ns | 7916795 126 | UUIDLoad | 64 ns | 63 ns | 10837304 127 | UUIDParse | 320 ns | 316 ns | 2206306 128 | UUIDParseInPlace | 317 ns | 313 ns | 2222561 129 | UUIDEqual | 50 ns | 49 ns | 12978765 130 | UUIDCompare | 65 ns | 65 ns | 10672186 131 | 132 | 133 | ## Building 134 | 135 | This project uses CMake to build tests and benchmarks. 136 | If you do not have googletest and googlebenchmark installed globally 137 | ``` 138 | git clone --recurse-submodules https://github.com/crashoz/uuid_v4.git 139 | ``` 140 | 141 | If you want to run the benchmark against the other libraries you need to install them (`libuuid` and `boost`) 142 | 143 | otherwise 144 | ``` 145 | git clone https://github.com/crashoz/uuid_v4.git 146 | ``` 147 | 148 | Then build 149 | ``` 150 | mkdir build 151 | cd build 152 | cmake -Dtest=ON -Dbenchmark=ON .. 153 | cmake --build . 154 | ./tests/uuid_v4_test 155 | ./benchmarks/uuid_v4_benchmark 156 | ``` 157 | 158 | [RFC-4122]: https://tools.ietf.org/html/rfc4122 159 | [random]: https://en.cppreference.com/w/cpp/header/random 160 | [basic]: https://gist.github.com/fernandomv3/46a6d7656f50ee8d39dc 161 | [libuuid]: https://linux.die.net/man/3/libuuid 162 | [boost]: https://www.boost.org/doc/libs/1_68_0/libs/uuid/doc/index.html 163 | -------------------------------------------------------------------------------- /benchmarks/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories (${uuid_v4_SOURCE_DIR}) 2 | 3 | add_executable (uuid_v4_benchmark uuid_v4_benchmark.cpp) 4 | target_link_libraries (uuid_v4_benchmark benchmark::benchmark) 5 | add_test(NAME uuid_v4_benchmark COMMAND uuid_v4_benchmark) 6 | 7 | add_executable (compare_benchmark compare_benchmark.cpp) 8 | target_link_libraries (compare_benchmark benchmark::benchmark uuid) 9 | add_test(NAME compare_benchmark COMMAND compare_benchmark) 10 | -------------------------------------------------------------------------------- /benchmarks/basic.h: -------------------------------------------------------------------------------- 1 | #ifndef UUID_H 2 | #define UUID_H 3 | 4 | #include 5 | #include 6 | 7 | // https://gist.github.com/fernandomv3/46a6d7656f50ee8d39dc 8 | //*Adapted from https://gist.github.com/ne-sachirou/882192 9 | //*std::rand() can be replaced with other algorithms as Xorshift for better perfomance 10 | //*Random seed must be initialized by user 11 | 12 | namespace MathUtils{ 13 | const std::string CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 14 | 15 | std::string generateUUID(){ 16 | std::string uuid = std::string(36,' '); 17 | int rnd = 0; 18 | int r = 0; 19 | 20 | uuid[8] = '-'; 21 | uuid[13] = '-'; 22 | uuid[18] = '-'; 23 | uuid[23] = '-'; 24 | 25 | uuid[14] = '4'; 26 | 27 | for(int i=0;i<36;i++){ 28 | if (i != 8 && i != 13 && i != 18 && i != 14 && i != 23) { 29 | if (rnd <= 0x02) { 30 | rnd = 0x2000000 + (std::rand() * 0x1000000) | 0; 31 | } 32 | rnd >>= 4; 33 | uuid[i] = CHARS[(i == 19) ? ((rnd & 0xf) & 0x3) | 0x8 : rnd & 0xf]; 34 | } 35 | } 36 | return uuid; 37 | } 38 | 39 | } 40 | #endif 41 | -------------------------------------------------------------------------------- /benchmarks/compare_benchmark.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "uuid_v4.h" 4 | #include "basic.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | #define SCALE 100 12 | #define LOOP_START for(int i=0;i uuidGenerator; 65 | UUIDv4::UUID uuid; 66 | for (auto _ : state) { 67 | LOOP_START 68 | uuid = uuidGenerator.getUUID(); 69 | LOOP_END 70 | } 71 | } 72 | BENCHMARK(UUID_v4); 73 | 74 | BENCHMARK_MAIN(); 75 | -------------------------------------------------------------------------------- /benchmarks/uuid_v4_benchmark.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "uuid_v4.h" 4 | 5 | #define SCALE 100 6 | #define LOOP_START for(int i=0;i uuidGenerator; 13 | for (auto _ : state) { 14 | LOOP_START 15 | UUIDv4::UUID uuid = uuidGenerator.getUUID(); 16 | ((void)uuid); // Prevents unused warning 17 | LOOP_END 18 | } 19 | } 20 | BENCHMARK(UUIDGeneration); 21 | 22 | static void UUIDSerializeAlloc(benchmark::State& state) { 23 | UUIDv4::UUIDGenerator uuidGenerator; 24 | UUIDv4::UUID uuid = uuidGenerator.getUUID(); 25 | for (auto _ : state) { 26 | LOOP_START 27 | std::string bytes = uuid.bytes(); 28 | LOOP_END 29 | } 30 | } 31 | BENCHMARK(UUIDSerializeAlloc); 32 | 33 | static void UUIDSerializeByRef(benchmark::State& state) { 34 | UUIDv4::UUIDGenerator uuidGenerator; 35 | UUIDv4::UUID uuid = uuidGenerator.getUUID(); 36 | std::string bytes; 37 | for (auto _ : state) { 38 | LOOP_START 39 | uuid.bytes(bytes); 40 | LOOP_END 41 | } 42 | } 43 | BENCHMARK(UUIDSerializeByRef); 44 | 45 | static void UUIDSerializeCharArray(benchmark::State& state) { 46 | UUIDv4::UUIDGenerator uuidGenerator; 47 | UUIDv4::UUID uuid = uuidGenerator.getUUID(); 48 | char bytes[16]; 49 | for (auto _ : state) { 50 | LOOP_START 51 | uuid.bytes(bytes); 52 | LOOP_END 53 | } 54 | } 55 | BENCHMARK(UUIDSerializeCharArray); 56 | 57 | static void UUIDPretty(benchmark::State& state) { 58 | UUIDv4::UUIDGenerator uuidGenerator; 59 | UUIDv4::UUID uuid = uuidGenerator.getUUID(); 60 | std::string pretty; 61 | for (auto _ : state) { 62 | LOOP_START 63 | pretty = uuid.str(); 64 | LOOP_END 65 | } 66 | } 67 | BENCHMARK(UUIDPretty); 68 | 69 | static void UUIDPrettyByRef(benchmark::State& state) { 70 | UUIDv4::UUIDGenerator uuidGenerator; 71 | UUIDv4::UUID uuid = uuidGenerator.getUUID(); 72 | std::string pretty; 73 | pretty.resize(36); 74 | for (auto _ : state) { 75 | LOOP_START 76 | benchmark::DoNotOptimize(pretty.data()); 77 | uuid.str(pretty); 78 | benchmark::ClobberMemory(); 79 | LOOP_END 80 | } 81 | } 82 | BENCHMARK(UUIDPrettyByRef); 83 | 84 | static void UUIDPrettyCharArray(benchmark::State& state) { 85 | UUIDv4::UUIDGenerator uuidGenerator; 86 | UUIDv4::UUID uuid = uuidGenerator.getUUID(); 87 | char* pretty = new char[36]; 88 | for (auto _ : state) { 89 | LOOP_START 90 | benchmark::DoNotOptimize(pretty); 91 | uuid.str(pretty); 92 | benchmark::ClobberMemory(); 93 | LOOP_END 94 | } 95 | delete[] pretty; 96 | } 97 | BENCHMARK(UUIDPrettyCharArray); 98 | 99 | static void UUIDLoad(benchmark::State& state) { 100 | UUIDv4::UUIDGenerator uuidGenerator; 101 | UUIDv4::UUID uuid = uuidGenerator.getUUID(); 102 | std::string bytes = uuid.bytes(); 103 | UUIDv4::UUID uuidNew; 104 | for (auto _ : state) { 105 | LOOP_START 106 | benchmark::DoNotOptimize(uuidNew = UUIDv4::UUID(bytes)); 107 | LOOP_END 108 | } 109 | } 110 | BENCHMARK(UUIDLoad); 111 | 112 | static void UUIDParse(benchmark::State& state) { 113 | UUIDv4::UUIDGenerator uuidGenerator; 114 | UUIDv4::UUID uuid = uuidGenerator.getUUID(); 115 | std::string pretty = uuid.str(); 116 | const char *raw = pretty.c_str(); 117 | UUIDv4::UUID uuidNew; 118 | for (auto _ : state) { 119 | LOOP_START 120 | benchmark::DoNotOptimize(UUIDv4::UUID::fromStrFactory(raw)); 121 | LOOP_END 122 | } 123 | } 124 | BENCHMARK(UUIDParse); 125 | 126 | static void UUIDParseInPlace(benchmark::State& state) { 127 | UUIDv4::UUIDGenerator uuidGenerator; 128 | UUIDv4::UUID uuid = uuidGenerator.getUUID(); 129 | std::string pretty = uuid.str(); 130 | const char *raw = pretty.c_str(); 131 | UUIDv4::UUID uuidNew; 132 | for (auto _ : state) { 133 | LOOP_START 134 | benchmark::DoNotOptimize(uuidNew); 135 | uuidNew.fromStr(raw); 136 | benchmark::ClobberMemory(); 137 | LOOP_END 138 | } 139 | } 140 | BENCHMARK(UUIDParseInPlace); 141 | 142 | 143 | static void UUIDEqual(benchmark::State& state) { 144 | UUIDv4::UUIDGenerator uuidGenerator; 145 | UUIDv4::UUID uuid = uuidGenerator.getUUID(); 146 | UUIDv4::UUID uuid1 = uuidGenerator.getUUID(); 147 | for (auto _ : state) { 148 | LOOP_START 149 | benchmark::DoNotOptimize(uuid == uuid1); 150 | LOOP_END 151 | } 152 | } 153 | BENCHMARK(UUIDEqual); 154 | 155 | static void UUIDCompare(benchmark::State& state) { 156 | UUIDv4::UUIDGenerator uuidGenerator; 157 | UUIDv4::UUID uuid = uuidGenerator.getUUID(); 158 | UUIDv4::UUID uuid1 = uuidGenerator.getUUID(); 159 | for (auto _ : state) { 160 | LOOP_START 161 | benchmark::DoNotOptimize(uuid < uuid1); 162 | LOOP_END 163 | } 164 | } 165 | BENCHMARK(UUIDCompare); 166 | 167 | BENCHMARK_MAIN(); 168 | -------------------------------------------------------------------------------- /endianness.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(__GLIBC__) || defined(__GNU_LIBRARY__) || defined(__ANDROID__) 4 | #include 5 | #elif defined(__APPLE__) && defined(__MACH__) 6 | #include 7 | #elif defined(BSD) || defined(_SYSTYPE_BSD) 8 | #if defined(__OpenBSD__) 9 | #include 10 | #else 11 | #include 12 | #endif 13 | #endif 14 | 15 | #if defined(__BYTE_ORDER) 16 | #if defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) 17 | #define BIGENDIAN 18 | #elif defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN) 19 | #define LITTLEENDIAN 20 | #endif 21 | #elif defined(_BYTE_ORDER) 22 | #if defined(_BIG_ENDIAN) && (_BYTE_ORDER == _BIG_ENDIAN) 23 | #define BIGENDIAN 24 | #elif defined(_LITTLE_ENDIAN) && (_BYTE_ORDER == _LITTLE_ENDIAN) 25 | #define LITTLEENDIAN 26 | #endif 27 | #elif defined(__BIG_ENDIAN__) 28 | #define BIGENDIAN 29 | #elif defined(__LITTLE_ENDIAN__) 30 | #define LITTLEENDIAN 31 | #else 32 | #if defined(__ARMEL__) || \ 33 | defined(__THUMBEL__) || \ 34 | defined(__AARCH64EL__) || \ 35 | defined(_MIPSEL) || \ 36 | defined(__MIPSEL) || \ 37 | defined(__MIPSEL__) || \ 38 | defined(__ia64__) || defined(_IA64) || \ 39 | defined(__IA64__) || defined(__ia64) || \ 40 | defined(_M_IA64) || defined(__itanium__) || \ 41 | defined(i386) || defined(__i386__) || \ 42 | defined(__i486__) || defined(__i586__) || \ 43 | defined(__i686__) || defined(__i386) || \ 44 | defined(_M_IX86) || defined(_X86_) || \ 45 | defined(__THW_INTEL__) || defined(__I86__) || \ 46 | defined(__INTEL__) || \ 47 | defined(__x86_64) || defined(__x86_64__) || \ 48 | defined(__amd64__) || defined(__amd64) || \ 49 | defined(_M_X64) || \ 50 | defined(__bfin__) || defined(__BFIN__) || \ 51 | defined(bfin) || defined(BFIN) 52 | 53 | #define LITTLEENDIAN 54 | #elif defined(__m68k__) || defined(M68000) || \ 55 | defined(__hppa__) || defined(__hppa) || defined(__HPPA__) || \ 56 | defined(__sparc__) || defined(__sparc) || \ 57 | defined(__370__) || defined(__THW_370__) || \ 58 | defined(__s390__) || defined(__s390x__) || \ 59 | defined(__SYSC_ZARCH__) 60 | 61 | #define BIGENDIAN 62 | 63 | #elif defined(__arm__) || defined(__arm64) || defined(__thumb__) || \ 64 | defined(__TARGET_ARCH_ARM) || defined(__TARGET_ARCH_THUMB) || \ 65 | defined(__ARM_ARCH) || \ 66 | defined(_M_ARM) || defined(_M_ARM64) 67 | 68 | #if defined(_WIN32) || defined(_WIN64) || \ 69 | defined(__WIN32__) || defined(__TOS_WIN__) || \ 70 | defined(__WINDOWS__) 71 | 72 | #define LITTLEENDIAN 73 | 74 | #else 75 | #error "Cannot determine system endianness." 76 | #endif 77 | #endif 78 | #endif 79 | 80 | 81 | #if defined(BIGENDIAN) 82 | // Try to use compiler intrinsics 83 | #if defined(__INTEL_COMPILER) || defined(__ICC) 84 | #define betole16(x) _bswap16(x) 85 | #define betole32(x) _bswap(x) 86 | #define betole64(x) _bswap64(x) 87 | #elif defined(__GNUC__) // GCC and CLANG 88 | #define betole16(x) __builtin_bswap16(x) 89 | #define betole32(x) __builtin_bswap32(x) 90 | #define betole64(x) __builtin_bswap64(x) 91 | #elif defined(_MSC_VER) // MSVC 92 | #include 93 | #define betole16(x) _byteswap_ushort(x) 94 | #define betole32(x) _byteswap_ulong(x) 95 | #define betole64(x) _byteswap_uint64(x) 96 | #else 97 | #define FALLBACK_SWAP 98 | #define betole16(x) swap_u16(x) 99 | #define betole32(x) swap_u32(x) 100 | #define betole64(x) swap_u64(x) 101 | #endif 102 | #define betole128(x) swap_u128(x) 103 | #define betole256(x) swap_u256(x) 104 | #else 105 | #define betole16(x) (x) 106 | #define betole32(x) (x) 107 | #define betole64(x) (x) 108 | #define betole128(x) (x) 109 | #define betole256(x) (x) 110 | #endif // BIGENDIAN 111 | 112 | #if defined(BIGENDIAN) 113 | #include 114 | #include 115 | #include 116 | #include 117 | 118 | inline __m128i swap_u128(__m128i value) { 119 | const __m128i shuffle = _mm_set_epi64x(0x0001020304050607, 0x08090a0b0c0d0e0f); 120 | return _mm_shuffle_epi8(value, shuffle); 121 | } 122 | 123 | inline __m256i swap_u256(__m256i value) { 124 | const __m256i shuffle = _mm256_set_epi64x(0x0001020304050607, 0x08090a0b0c0d0e0f, 0x0001020304050607, 0x08090a0b0c0d0e0f); 125 | return _mm256_shuffle_epi8(value, shuffle); 126 | } 127 | #endif // BIGENDIAN 128 | 129 | #if defined(FALLBACK_SWAP) 130 | #include 131 | inline uint16_t swap_u16(uint16_t value) 132 | { 133 | return 134 | ((value & 0xFF00u) >> 8u) | 135 | ((value & 0x00FFu) << 8u); 136 | } 137 | inline uint32_t swap_u32(uint32_t value) 138 | { 139 | return 140 | ((value & 0xFF000000u) >> 24u) | 141 | ((value & 0x00FF0000u) >> 8u) | 142 | ((value & 0x0000FF00u) << 8u) | 143 | ((value & 0x000000FFu) << 24u); 144 | } 145 | inline uint64_t swap_u64(uint64_t value) 146 | { 147 | return 148 | ((value & 0xFF00000000000000u) >> 56u) | 149 | ((value & 0x00FF000000000000u) >> 40u) | 150 | ((value & 0x0000FF0000000000u) >> 24u) | 151 | ((value & 0x000000FF00000000u) >> 8u) | 152 | ((value & 0x00000000FF000000u) << 8u) | 153 | ((value & 0x0000000000FF0000u) << 24u) | 154 | ((value & 0x000000000000FF00u) << 40u) | 155 | ((value & 0x00000000000000FFu) << 56u); 156 | } 157 | #endif // FALLBACK_SWAP 158 | -------------------------------------------------------------------------------- /example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "uuid_v4.h" 6 | 7 | #define IT 1000000000 8 | 9 | void debugUUID(const UUIDv4::UUID &uuid) { 10 | std::string bytes = uuid.bytes(); 11 | for (int i=0; i<16; i++) { 12 | printf("%02hhx", bytes[i]); 13 | } 14 | printf("\n"); 15 | } 16 | 17 | int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) { 18 | UUIDv4::UUIDGenerator uuidGenerator; 19 | char txt[37]; 20 | auto t1 = std::chrono::high_resolution_clock::now(); 21 | for (int i=0; i(diff).count() / 1e9) << std::endl; 28 | 29 | UUIDv4::UUID load; 30 | std::cin >> load; 31 | std::cout << load << std::endl; 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories (${uuid_v4_SOURCE_DIR}) 2 | 3 | add_executable (uuid_v4_test uuid_v4_test.cpp) 4 | 5 | target_link_libraries (uuid_v4_test GTest::gtest_main) 6 | add_test(NAME uuid_v4_test COMMAND uuid_v4_test) 7 | -------------------------------------------------------------------------------- /tests/uuid_v4_test.cpp: -------------------------------------------------------------------------------- 1 | #include "uuid_v4.h" 2 | #include "gtest/gtest.h" 3 | 4 | namespace { 5 | void pb(const std::string &s) { 6 | for (int i=0; i<16; i++) { 7 | printf("%02hhx", s[i]); 8 | } 9 | printf("\n"); 10 | } 11 | 12 | bool isBinaryLE(uint64_t x, uint64_t y, const std::string &bytes) { 13 | for (int i=0; i<8; i++) { 14 | if (static_cast(bytes[i]) != ((x >> i*8) & 0xFF)) { 15 | return false; 16 | } 17 | } 18 | for (int i=8; i<16; i++) { 19 | if (static_cast(bytes[i]) != ((y >> (i-8)*8) & 0xFF)) { 20 | return false; 21 | } 22 | } 23 | return true; 24 | } 25 | 26 | TEST(UUIDTest, SerializeUUIDinLE) { 27 | uint64_t x = 0x0012003400560078ull; 28 | uint64_t y = 0x0012003400560078ull; 29 | UUIDv4::UUID uuid(x, y); 30 | std::string bytes = uuid.bytes(); 31 | ASSERT_PRED3(isBinaryLE, x, y, bytes); 32 | } 33 | 34 | TEST(UUIDTest, PrettyPrints) { 35 | uint8_t bytes[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; 36 | UUIDv4::UUID uuid(bytes); 37 | std::string pretty = uuid.str(); 38 | ASSERT_EQ(pretty, "00010203-0405-0607-0809-0a0b0c0d0e0f"); 39 | } 40 | 41 | TEST(UUIDTest, UnserializeFromLE) { 42 | std::string bytes = { 43 | 0x78, 0x00, 0x56, 0x00, 0x34, 0x00, 0x12, 0x00, 44 | 0x78, 0x00, 0x56, 0x00, 0x34, 0x00, 0x12, 0x00 45 | }; 46 | UUIDv4::UUID uuid(bytes); 47 | ASSERT_EQ(uuid.str(), "78005600-3400-1200-7800-560034001200"); 48 | } 49 | 50 | TEST(UUIDTest, ParsePretty) { 51 | std::string bytes = { 52 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 53 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f 54 | }; 55 | UUIDv4::UUID uuid = UUIDv4::UUID::fromStrFactory("00010203-0405-0607-0809-0a0b0c0d0e0f"); 56 | ASSERT_EQ(uuid.bytes(), bytes); 57 | } 58 | 59 | TEST(UUIDTest, StreamOperators) { 60 | UUIDv4::UUID uuid; 61 | std::string pretty = "00120034-0056-0078-0012-003400560078"; 62 | std::istringstream in(pretty); 63 | std::ostringstream out; 64 | 65 | in >> uuid; 66 | out << uuid; 67 | 68 | ASSERT_EQ(out.str(), pretty); 69 | } 70 | 71 | TEST(UUIDTest, Comparisons) { 72 | UUIDv4::UUID uuid = UUIDv4::UUID::fromStrFactory("00120034-0056-0078-0012-003400560078"); 73 | UUIDv4::UUID uuid2 = UUIDv4::UUID(uuid); 74 | 75 | ASSERT_TRUE(uuid == uuid2); 76 | ASSERT_FALSE(uuid < uuid2); 77 | 78 | UUIDv4::UUID uuid3 = UUIDv4::UUID::fromStrFactory("f0120034-0056-0078-0012-003400560078"); 79 | ASSERT_TRUE(uuid < uuid3); 80 | 81 | UUIDv4::UUID uuid4 = UUIDv4::UUID::fromStrFactory("00020034-0056-0078-0012-003400560078"); 82 | ASSERT_FALSE(uuid < uuid4); 83 | ASSERT_TRUE(uuid > uuid4); 84 | 85 | UUIDv4::UUID uuid5 = UUIDv4::UUID::fromStrFactory("fc120034-0056-0078-0012-003400560078"); 86 | ASSERT_TRUE(uuid < uuid5); 87 | ASSERT_FALSE(uuid > uuid5); 88 | } 89 | 90 | TEST(UUIDTest, HashTest) { 91 | UUIDv4::UUID uuid = UUIDv4::UUID::fromStrFactory("00120034-0056-0078-0012-003400560078"); 92 | UUIDv4::UUID uuid2 = UUIDv4::UUID(uuid); 93 | 94 | ASSERT_TRUE(uuid.hash() == uuid2.hash()); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /uuid_v4.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2018 Xavier "Crashoz" Launey 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "endianness.h" 22 | 23 | namespace UUIDv4 { 24 | 25 | /* 26 | Converts a 128-bits unsigned int to an UUIDv4 string representation. 27 | Uses SIMD via Intel's AVX2 instruction set. 28 | */ 29 | void inline m128itos(__m128i x, char* mem) { 30 | // Expand each byte in x to two bytes in res 31 | // i.e. 0x12345678 -> 0x0102030405060708 32 | // Then translate each byte to its hex ascii representation 33 | // i.e. 0x0102030405060708 -> 0x3132333435363738 34 | const __m256i mask = _mm256_set1_epi8(0x0F); 35 | const __m256i add = _mm256_set1_epi8(0x06); 36 | const __m256i alpha_mask = _mm256_set1_epi8(0x10); 37 | const __m256i alpha_offset = _mm256_set1_epi8(0x57); 38 | 39 | __m256i a = _mm256_castsi128_si256(x); 40 | __m256i as = _mm256_srli_epi64(a, 4); 41 | __m256i lo = _mm256_unpacklo_epi8(as, a); 42 | __m128i hi = _mm256_castsi256_si128(_mm256_unpackhi_epi8(as, a)); 43 | __m256i c = _mm256_inserti128_si256(lo, hi, 1); 44 | __m256i d = _mm256_and_si256(c, mask); 45 | __m256i alpha = _mm256_slli_epi64(_mm256_and_si256(_mm256_add_epi8(d, add), alpha_mask), 3); 46 | __m256i offset = _mm256_blendv_epi8(_mm256_slli_epi64(add, 3), alpha_offset, alpha); 47 | __m256i res = _mm256_add_epi8(d, offset); 48 | 49 | // Add dashes between blocks as specified in RFC-4122 50 | // 8-4-4-4-12 51 | const __m256i dash_shuffle = _mm256_set_epi32(0x0b0a0908, 0x07060504, 0x80030201, 0x00808080, 0x0d0c800b, 0x0a090880, 0x07060504, 0x03020100); 52 | const __m256i dash = _mm256_set_epi64x(0x0000000000000000ull, 0x2d000000002d0000ull, 0x00002d000000002d, 0x0000000000000000ull); 53 | 54 | __m256i resd = _mm256_shuffle_epi8(res, dash_shuffle); 55 | resd = _mm256_or_si256(resd, dash); 56 | 57 | _mm256_storeu_si256((__m256i*)mem, betole256(resd)); 58 | *(uint16_t*)(mem+16) = betole16(_mm256_extract_epi16(res, 7)); 59 | *(uint32_t*)(mem+32) = betole32(_mm256_extract_epi32(res, 7)); 60 | } 61 | 62 | /* 63 | Converts an UUIDv4 string representation to a 128-bits unsigned int. 64 | Uses SIMD via Intel's AVX2 instruction set. 65 | */ 66 | __m128i inline stom128i(const char* mem) { 67 | // Remove dashes and pack hex ascii bytes in a 256-bits int 68 | const __m256i dash_shuffle = _mm256_set_epi32(0x80808080, 0x0f0e0d0c, 0x0b0a0908, 0x06050403, 0x80800f0e, 0x0c0b0a09, 0x07060504, 0x03020100); 69 | 70 | __m256i x = betole256(_mm256_loadu_si256((__m256i*)mem)); 71 | x = _mm256_shuffle_epi8(x, dash_shuffle); 72 | x = _mm256_insert_epi16(x, betole16(*(uint16_t*)(mem+16)), 7); 73 | x = _mm256_insert_epi32(x, betole32(*(uint32_t*)(mem+32)), 7); 74 | 75 | // Build a mask to apply a different offset to alphas and digits 76 | const __m256i sub = _mm256_set1_epi8(0x2F); 77 | const __m256i mask = _mm256_set1_epi8(0x20); 78 | const __m256i alpha_offset = _mm256_set1_epi8(0x28); 79 | const __m256i digits_offset = _mm256_set1_epi8(0x01); 80 | const __m256i unweave = _mm256_set_epi32(0x0f0d0b09, 0x0e0c0a08, 0x07050301, 0x06040200, 0x0f0d0b09, 0x0e0c0a08, 0x07050301, 0x06040200); 81 | const __m256i shift = _mm256_set_epi32(0x00000000, 0x00000004, 0x00000000, 0x00000004, 0x00000000, 0x00000004, 0x00000000, 0x00000004); 82 | 83 | // Translate ascii bytes to their value 84 | // i.e. 0x3132333435363738 -> 0x0102030405060708 85 | // Shift hi-digits 86 | // i.e. 0x0102030405060708 -> 0x1002300450067008 87 | // Horizontal add 88 | // i.e. 0x1002300450067008 -> 0x12345678 89 | __m256i a = _mm256_sub_epi8(x, sub); 90 | __m256i alpha = _mm256_slli_epi64(_mm256_and_si256(a, mask), 2); 91 | __m256i sub_mask = _mm256_blendv_epi8(digits_offset, alpha_offset, alpha); 92 | a = _mm256_sub_epi8(a, sub_mask); 93 | a = _mm256_shuffle_epi8(a, unweave); 94 | a = _mm256_sllv_epi32(a, shift); 95 | a = _mm256_hadd_epi32(a, _mm256_setzero_si256()); 96 | a = _mm256_permute4x64_epi64(a, 0b00001000); 97 | 98 | return _mm256_castsi256_si128(a); 99 | } 100 | 101 | /* 102 | * UUIDv4 (random 128-bits) RFC-4122 103 | */ 104 | class UUID { 105 | public: 106 | UUID() 107 | {} 108 | 109 | UUID(const UUID &other) { 110 | __m128i x = _mm_load_si128((__m128i*)other.data); 111 | _mm_store_si128((__m128i*)data, x); 112 | } 113 | 114 | /* Builds a 128-bits UUID */ 115 | UUID(__m128i uuid) { 116 | _mm_store_si128((__m128i*)data, uuid); 117 | } 118 | 119 | UUID(uint64_t x, uint64_t y) { 120 | __m128i z = _mm_set_epi64x(x, y); 121 | _mm_store_si128((__m128i*)data, z); 122 | } 123 | 124 | UUID(const uint8_t* bytes) { 125 | __m128i x = _mm_loadu_si128((__m128i*)bytes); 126 | _mm_store_si128((__m128i*)data, x); 127 | } 128 | 129 | /* Builds an UUID from a byte string (16 bytes long) */ 130 | explicit UUID(const std::string &bytes) { 131 | __m128i x = betole128(_mm_loadu_si128((__m128i*)bytes.data())); 132 | _mm_store_si128((__m128i*)data, x); 133 | } 134 | 135 | /* Static factory to parse an UUID from its string representation */ 136 | static UUID fromStrFactory(const std::string &s) { 137 | return fromStrFactory(s.c_str()); 138 | } 139 | 140 | static UUID fromStrFactory(const char* raw) { 141 | return UUID(stom128i(raw)); 142 | } 143 | 144 | void fromStr(const char* raw) { 145 | _mm_store_si128((__m128i*)data, stom128i(raw)); 146 | } 147 | 148 | UUID& operator=(const UUID &other) { 149 | if (&other == this) { 150 | return *this; 151 | } 152 | __m128i x = _mm_load_si128((__m128i*)other.data); 153 | _mm_store_si128((__m128i*)data, x); 154 | return *this; 155 | } 156 | 157 | friend bool operator==(const UUID &lhs, const UUID &rhs) { 158 | __m128i x = _mm_load_si128((__m128i*)lhs.data); 159 | __m128i y = _mm_load_si128((__m128i*)rhs.data); 160 | 161 | __m128i neq = _mm_xor_si128(x, y); 162 | return _mm_test_all_zeros(neq, neq); 163 | } 164 | 165 | friend bool operator<(const UUID &lhs, const UUID &rhs) { 166 | // There are no trivial 128-bits comparisons in SSE/AVX 167 | // It's faster to compare two uint64_t 168 | uint64_t *x = (uint64_t*)lhs.data; 169 | uint64_t *y = (uint64_t*)rhs.data; 170 | return *x < *y || (*x == *y && *(x + 1) < *(y + 1)); 171 | } 172 | 173 | friend bool operator!=(const UUID &lhs, const UUID &rhs) { return !(lhs == rhs); } 174 | friend bool operator> (const UUID &lhs, const UUID &rhs) { return rhs < lhs; } 175 | friend bool operator<=(const UUID &lhs, const UUID &rhs) { return !(lhs > rhs); } 176 | friend bool operator>=(const UUID &lhs, const UUID &rhs) { return !(lhs < rhs); } 177 | 178 | /* Serializes the uuid to a byte string (16 bytes) */ 179 | std::string bytes() const { 180 | std::string mem; 181 | bytes(mem); 182 | return mem; 183 | } 184 | 185 | void bytes(std::string &out) const { 186 | out.resize(sizeof(data)); 187 | bytes((char*)out.data()); 188 | } 189 | 190 | void bytes(char* bytes) const { 191 | __m128i x = betole128(_mm_load_si128((__m128i*)data)); 192 | _mm_storeu_si128((__m128i*)bytes, x); 193 | } 194 | 195 | /* Converts the uuid to its string representation */ 196 | std::string str() const { 197 | std::string mem; 198 | str(mem); 199 | return mem; 200 | } 201 | 202 | void str(std::string &s) const { 203 | s.resize(36); 204 | str((char*)s.data()); 205 | } 206 | 207 | void str(char *res) const { 208 | __m128i x = _mm_load_si128((__m128i*)data); 209 | m128itos(x, res); 210 | } 211 | 212 | friend std::ostream& operator<< (std::ostream& stream, const UUID& uuid) { 213 | return stream << uuid.str(); 214 | } 215 | 216 | friend std::istream& operator>> (std::istream& stream, UUID& uuid) { 217 | std::string s; 218 | stream >> s; 219 | uuid = fromStrFactory(s); 220 | return stream; 221 | } 222 | 223 | size_t hash() const { 224 | const uint64_t a = *((uint64_t*)data); 225 | const uint64_t b = *((uint64_t*)&data[8]); 226 | return a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2)); 227 | } 228 | 229 | private: 230 | alignas(16) uint8_t data[16]; 231 | }; 232 | 233 | /* 234 | Generates UUIDv4 from a provided random generator (c++11 module) 235 | std::mt19937_64 is highly recommended as it has a SIMD implementation that 236 | makes it very fast and it produces high quality randomness. 237 | */ 238 | template 239 | class UUIDGenerator { 240 | public: 241 | UUIDGenerator() : generator(new RNG(std::random_device()())), distribution((std::numeric_limits::min)(), (std::numeric_limits::max)()) 242 | {} 243 | 244 | UUIDGenerator(uint64_t seed) : generator(new RNG(seed)), distribution((std::numeric_limits::min)(), (std::numeric_limits::max)()) 245 | {} 246 | 247 | UUIDGenerator(RNG& gen) : generator(gen), distribution((std::numeric_limits::min)(), (std::numeric_limits::max)()) 248 | {} 249 | 250 | /* Generates a new UUID */ 251 | UUID getUUID() { 252 | // The two masks set the uuid version (4) and variant (1) 253 | const __m128i and_mask = _mm_set_epi64x(0xFFFFFFFFFFFFFF3Full, 0xFF0FFFFFFFFFFFFFull); 254 | const __m128i or_mask = _mm_set_epi64x(0x0000000000000080ull, 0x0040000000000000ull); 255 | __m128i n = _mm_set_epi64x(distribution(*generator), distribution(*generator)); 256 | __m128i uuid = _mm_or_si128(_mm_and_si128(n, and_mask), or_mask); 257 | 258 | return UUID(uuid); 259 | } 260 | 261 | private: 262 | std::shared_ptr generator; 263 | std::uniform_int_distribution distribution; 264 | }; 265 | 266 | } 267 | 268 | namespace std { 269 | template <> struct hash 270 | { 271 | size_t operator()(const UUIDv4::UUID & uuid) const 272 | { 273 | return uuid.hash(); 274 | } 275 | }; 276 | } 277 | --------------------------------------------------------------------------------