├── .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 |
--------------------------------------------------------------------------------