├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── P0959.md ├── README.md ├── appveyor.yml ├── catch ├── catch.hpp ├── catch_reporter_automake.hpp ├── catch_reporter_sonarqube.hpp ├── catch_reporter_tap.hpp └── catch_reporter_teamcity.hpp ├── cmake ├── Config.cmake.in └── FindLibuuid.cmake ├── gsl ├── gsl ├── gsl_algorithm ├── gsl_assert ├── gsl_byte ├── gsl_util ├── multi_span ├── pointers ├── span └── string_span ├── how_to_build.md ├── include └── uuid.h ├── lgtm.yml └── test ├── CMakeLists.txt ├── main.cpp ├── test_generators.cpp └── test_uuid.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | /build/.gitignore 34 | 35 | build 36 | /test/CMakeFiles/3.10.0/CompilerIdC/Debug/CompilerIdC.tlog 37 | /.vs 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: gcc 3 | 4 | addons: 5 | apt: 6 | sources: 7 | - ubuntu-toolchain-r-test 8 | packages: 9 | - cppcheck 10 | - g++-8 11 | - uuid-dev 12 | 13 | before_install: 14 | - sudo apt-cache search libuuid 15 | 16 | install: 17 | - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90 18 | 19 | before_script: 20 | - cd ${TRAVIS_BUILD_DIR} 21 | - cmake -H. -BBuild -DCMAKE_BUILD_TYPE=Release -Wdev 22 | - cd Build 23 | 24 | script: 25 | - make -j 2 26 | - ctest -V -j 2 -C Release 27 | - cppcheck --quiet --error-exitcode=1 . 28 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7.0) 2 | 3 | set(UUID_MAIN_PROJECT OFF) 4 | if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 5 | set(UUID_MAIN_PROJECT ON) 6 | endif() 7 | 8 | project(stduuid CXX) 9 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") 10 | 11 | option(UUID_BUILD_TESTS "Build the unit tests" ${UUID_MAIN_PROJECT}) 12 | option(UUID_SYSTEM_GENERATOR "Enable operating system uuid generator" OFF) 13 | option(UUID_TIME_GENERATOR "Enable experimental time-based uuid generator" OFF) 14 | option(UUID_USING_CXX20_SPAN "Using span from std instead of gsl" OFF) 15 | option(UUID_ENABLE_INSTALL "Create an install target" ${UUID_MAIN_PROJECT}) 16 | 17 | # Library target 18 | add_library(${PROJECT_NAME} INTERFACE) 19 | target_include_directories(${PROJECT_NAME} INTERFACE 20 | $ 21 | $) 22 | 23 | # Using system uuid generator 24 | if (UUID_SYSTEM_GENERATOR) 25 | target_compile_definitions(${PROJECT_NAME} INTERFACE UUID_SYSTEM_GENERATOR) 26 | 27 | if (WIN32) 28 | elseif (APPLE) 29 | find_library(CFLIB CoreFoundation REQUIRED) 30 | target_link_libraries(${PROJECT_NAME} INTERFACE ${CFLIB}) 31 | else () 32 | find_package(Libuuid REQUIRED) 33 | if (Libuuid_FOUND) 34 | target_include_directories(${PROJECT_NAME} INTERFACE ${Libuuid_INCLUDE_DIRS}) 35 | target_link_libraries(${PROJECT_NAME} INTERFACE ${Libuuid_LIBRARIES}) 36 | endif () 37 | endif () 38 | endif () 39 | 40 | # Using time-based generator 41 | if (UUID_TIME_GENERATOR) 42 | target_compile_definitions(${PROJECT_NAME} INTERFACE UUID_TIME_GENERATOR) 43 | endif() 44 | 45 | # Using span from std 46 | if (NOT UUID_USING_CXX20_SPAN) 47 | target_include_directories(${PROJECT_NAME} INTERFACE 48 | $ 49 | $) 50 | install(DIRECTORY gsl DESTINATION include) 51 | endif () 52 | 53 | if(UUID_ENABLE_INSTALL) 54 | # Install step and imported target 55 | install(FILES include/uuid.h DESTINATION include) 56 | install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}-targets) 57 | install(EXPORT ${PROJECT_NAME}-targets 58 | DESTINATION lib/cmake/${PROJECT_NAME}) 59 | 60 | # Config files for find_package() 61 | include(CMakePackageConfigHelpers) 62 | configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in 63 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" 64 | INSTALL_DESTINATION lib/cmake/${PROJECT_NAME}) 65 | write_basic_package_version_file( 66 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-version.cmake" 67 | VERSION "1.0" 68 | COMPATIBILITY AnyNewerVersion) 69 | install(FILES 70 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" 71 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-version.cmake" 72 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindLibuuid.cmake" 73 | DESTINATION lib/cmake/${PROJECT_NAME}) 74 | export(EXPORT ${PROJECT_NAME}-targets 75 | FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}-targets.cmake") 76 | endif() 77 | 78 | # Tests 79 | if (UUID_BUILD_TESTS) 80 | enable_testing() 81 | add_subdirectory(test) 82 | endif () 83 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 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 | # stduuid 2 | A C++17 cross-platform single-header library implementation **for universally unique identifiers**, simply know as either UUID or GUID (mostly on Windows). A UUID is a 128-bit number used to uniquely identify information in computer systems, such as database table keys, COM interfaces, classes and type libraries, and many others. 3 | 4 | [![Build Status](https://travis-ci.org/mariusbancila/stduuid.svg?branch=master)](https://travis-ci.org/mariusbancila/stduuid) 5 | [![Tests status](https://ci.appveyor.com/api/projects/status/0kw1n2s2xqxu5m62?svg=true&pendingText=tests%20-%20pending&failingText=tests%20-%20FAILED&passingText=tests%20-%20OK)](https://ci.appveyor.com/project/mariusbancila/stduuid) 6 | 7 | For information about UUID/GUIDs see: 8 | * [Universally unique identifier](https://en.wikipedia.org/wiki/Universally_unique_identifier) 9 | * [A Universally Unique IDentifier (UUID) URN Namespace](https://www.ietf.org/rfc/rfc4122.txt) 10 | 11 | ## Library overview 12 | Although the specification puts the uuid library in the `std` namespace, this implementation uses the namespace `uuids` for this purpose, in order to make the library usable without violating the restrictions imposed on the `std` namespace. The following types and utilities are available: 13 | 14 | Basic types: 15 | 16 | | Name | Description | 17 | | ---- | ----------- | 18 | | `uuid` | a class representing a UUID; this can be default constructed (a nil UUID), constructed from a range (defined by a pair of iterators), or from a `span`. | 19 | | `uuid_variant` | a strongly type enum representing the type of a UUID | 20 | | `uuid_version` | a strongly type enum representing the version of a UUID | 21 | 22 | Generators: 23 | 24 | | Name | Description | 25 | | ---- | ----------- | 26 | | `basic_uuid_random_generator` | a function object that generates version 4 UUIDs using a pseudo-random number generator engine. | 27 | | `uuid_random_generator` | a `basic_uuid_random_generator` using the Mersenne Twister engine (`basic_uuid_random_generator`) | 28 | | `uuid_name_generator` | a function object that generates version 5, name-based UUIDs using SHA1 hashing. | 29 | | `uuid_system_generator` | a function object that generates new UUIDs using operating system resources (`CoCreateGuid` on Windows, `uuid_generate` on Linux, `CFUUIDCreate` on Mac)

**Note**: This is not part of the standard proposal. It is available only if the `UUID_SYSTEM_GENERATOR` macro is defined. | 30 | | `uuid_time_generator` | an experimental function object that generates time-based UUIDs.

**Note**:This is an experimental feature and should not be used in any production code. It is available only if the `UUID_TIME_GENERATOR` macro is defined. | 31 | 32 | Utilities: 33 | 34 | | Name | Description | 35 | | ---- | ----------- | 36 | | `std::swap<>` | specialization of `swap` for `uuid` | 37 | | `std::hash<>` | specialization of `hash` for `uuid` (necessary for storing UUIDs in unordered associative containers, such as `std::unordered_set`) | 38 | 39 | Constants: 40 | 41 | | Name | Description | 42 | | ---- | ----------- | 43 | | `uuid_namespace_dns` | Namespace ID for name-based uuids when name string is a fully-qualified domain name. | 44 | | `uuid_namespace_url` | Namespace ID for name-based uuids when name string is a URL. | 45 | | `uuid_namespace_oid` | Namespace ID for name-based uuids when mame string is an ISO OID (See https://oidref.com/, https://en.wikipedia.org/wiki/Object_identifier). | 46 | | `uuid_namespace_x500` | Namespace ID for name-based uuids when name string is an X.500 DN, in DER or a text output format (See https://en.wikipedia.org/wiki/X.500, https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One). | 47 | 48 | Other: 49 | 50 | | Name | Description | 51 | | ---- | ----------- | 52 | | `operator==` and `operator!=` | for UUIDs comparison for equality/inequality | 53 | | `operator<` | for comparing whether one UUIDs is less than another. Although this operation does not make much logical sense, it is necessary in order to store UUIDs in a std::set. | 54 | | `operator<<` | to write a UUID to an output stream using the canonical textual representation. | 55 | | `to_string()` | creates a string with the canonical textual representation of a UUID. | 56 | 57 | ## Library history 58 | This library is an implementation of the proposal [P0959](P0959.md). 59 | 60 | **As the proposal evolves based on the standard committee and the C++ community feedback, this library implementation will reflect those changes.** 61 | 62 | See the revision history of the proposal for history of changes. 63 | 64 | ## Using the library 65 | The following is a list of examples for using the library: 66 | * Creating a nil UUID 67 | 68 | ```cpp 69 | uuid empty; 70 | assert(empty.is_nil()); 71 | ``` 72 | 73 | * Creating a new UUID 74 | 75 | ```cpp 76 | uuid const id = uuids::uuid_system_generator{}(); 77 | assert(!id.is_nil()); 78 | assert(id.version() == uuids::uuid_version::random_number_based); 79 | assert(id.variant() == uuids::uuid_variant::rfc); 80 | ``` 81 | 82 | * Creating a new UUID with a default random generator 83 | 84 | ```cpp 85 | std::random_device rd; 86 | auto seed_data = std::array {}; 87 | std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); 88 | std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); 89 | std::mt19937 generator(seq); 90 | uuids::uuid_random_generator gen{generator}; 91 | 92 | uuid const id = gen(); 93 | assert(!id.is_nil()); 94 | assert(id.as_bytes().size() == 16); 95 | assert(id.version() == uuids::uuid_version::random_number_based); 96 | assert(id.variant() == uuids::uuid_variant::rfc); 97 | ``` 98 | 99 | * Creating a new UUID with a particular random generator 100 | 101 | ```cpp 102 | std::random_device rd; 103 | auto seed_data = std::array {}; 104 | std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); 105 | std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); 106 | std::ranlux48_base generator(seq); 107 | 108 | uuids::basic_uuid_random_generator gen(&generator); 109 | uuid const id = gen(); 110 | assert(!id.is_nil()); 111 | assert(id.as_bytes().size() == 16); 112 | assert(id.version() == uuids::uuid_version::random_number_based); 113 | assert(id.variant() == uuids::uuid_variant::rfc); 114 | ``` 115 | 116 | * Creating a new UUID with the name generator 117 | 118 | ```cpp 119 | uuids::uuid_name_generator gen(uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()); 120 | uuid const id = gen("john"); 121 | 122 | assert(!id.is_nil()); 123 | assert(id.version() == uuids::uuid_version::name_based_sha1); 124 | assert(id.variant() == uuids::uuid_variant::rfc); 125 | ``` 126 | 127 | * Create a UUID from a string 128 | 129 | ```cpp 130 | auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; 131 | auto id = uuids::uuid::from_string(str); 132 | assert(id.has_value()); 133 | assert(uuids::to_string(id.value()) == str); 134 | 135 | // or 136 | 137 | auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s; 138 | uuid id = uuids::uuid::from_string(str).value(); 139 | assert(uuids::to_string(id) == str); 140 | ``` 141 | 142 | * Creating a UUID from a sequence of 16 bytes 143 | 144 | ```cpp 145 | std::array arr{{ 146 | 0x47, 0x18, 0x38, 0x23, 147 | 0x25, 0x74, 148 | 0x4b, 0xfd, 149 | 0xb4, 0x11, 150 | 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43}}; 151 | uuid id(arr); 152 | 153 | assert(uuids::to_string(id) == "47183823-2574-4bfd-b411-99ed177d3e43"); 154 | 155 | // or 156 | 157 | uuids::uuid::value_type arr[16] = { 158 | 0x47, 0x18, 0x38, 0x23, 159 | 0x25, 0x74, 160 | 0x4b, 0xfd, 161 | 0xb4, 0x11, 162 | 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 }; 163 | uuid id(std::begin(arr), std::end(arr)); 164 | assert(uuids::to_string(id) == "47183823-2574-4bfd-b411-99ed177d3e43"); 165 | 166 | // or 167 | 168 | uuids::uuid id{{ 169 | 0x47, 0x18, 0x38, 0x23, 170 | 0x25, 0x74, 171 | 0x4b, 0xfd, 172 | 0xb4, 0x11, 173 | 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43}}; 174 | 175 | assert(uuids::to_string(id) == "47183823-2574-4bfd-b411-99ed177d3e43"); 176 | ``` 177 | 178 | * Comparing UUIDs 179 | 180 | ```cpp 181 | uuid empty; 182 | uuid id = uuids::uuid_system_generator{}(); 183 | assert(empty == empty); 184 | assert(id == id); 185 | assert(empty != id); 186 | ``` 187 | 188 | * Swapping UUIDs 189 | 190 | ```cpp 191 | uuid empty; 192 | uuid id = uuids::uuid_system_generator{}(); 193 | 194 | assert(empty.is_nil()); 195 | assert(!id.is_nil()); 196 | 197 | std::swap(empty, id); 198 | 199 | assert(!empty.is_nil()); 200 | assert(id.is_nil()); 201 | 202 | empty.swap(id); 203 | 204 | assert(empty.is_nil()); 205 | assert(!id.is_nil()); 206 | ``` 207 | 208 | * Converting to string 209 | 210 | ```cpp 211 | uuid empty; 212 | assert(uuids::to_string(empty) == "00000000-0000-0000-0000-000000000000"); 213 | assert(uuids::to_string(empty) == L"00000000-0000-0000-0000-000000000000"); 214 | ``` 215 | 216 | * Using with an orderered associative container 217 | 218 | ```cpp 219 | std::random_device rd; 220 | auto seed_data = std::array {}; 221 | std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); 222 | std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); 223 | std::mt19937 engine(seq); 224 | uuids::uuid_random_generator gen(&engine); 225 | 226 | std::set ids{uuid{}, gen(), gen(), gen(), gen()}; 227 | 228 | assert(ids.size() == 5); 229 | assert(ids.find(uuid{}) != ids.end()); 230 | ``` 231 | 232 | * Using in an unordered associative container 233 | 234 | ```cpp 235 | std::random_device rd; 236 | auto seed_data = std::array {}; 237 | std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); 238 | std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); 239 | std::mt19937 engine(seq); 240 | uuids::uuid_random_generator gen(&engine); 241 | 242 | std::unordered_set ids{uuid{}, gen(), gen(), gen(), gen()}; 243 | 244 | assert(ids.size() == 5); 245 | assert(ids.find(uuid{}) != ids.end()); 246 | ``` 247 | 248 | * Hashing UUIDs 249 | 250 | ```cpp 251 | using namespace std::string_literals; 252 | auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; 253 | uuid id = uuids::uuid::from_string(str).value(); 254 | 255 | auto h1 = std::hash{}; 256 | auto h2 = std::hash{}; 257 | assert(h1(str) == h2(id)); 258 | ``` 259 | 260 | ### Random uuids 261 | 262 | If you generate uuids using the `basic_uuid_random_generator` and [std::random_device](https://en.cppreference.com/w/cpp/numeric/random/random_device) to seed a generator, keep in mind that this might not be non-deterministic and actually generate the same sequence of numbers: 263 | 264 | > std::random_device may be implemented in terms of an implementation-defined pseudo-random number engine if a non-deterministic source (e.g. a hardware device) is not available to the implementation. In this case each std::random_device object may generate the same number sequence. 265 | 266 | This could be a problem with MinGW. See [Bug 85494 - implementation of random_device on mingw is useless](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85494). This was fixed in GCC 9.2. 267 | 268 | A portable alternative is to use the [Boost.Random](https://www.boost.org/doc/libs/1_78_0/doc/html/boost_random.html) library. 269 | 270 | ## Support 271 | The library is supported on all major operating systems: Windows, Linux and Mac OS. 272 | 273 | ## Dependencies 274 | If you use the library in a project built with C++20, then you can use `std::span`. This is used by default, if the header is supported by your compiler. The check is done with the [__cpp_lib_span](https://en.cppreference.com/w/cpp/utility/feature_test) feature-test macro. 275 | 276 | Otherwise, such as when building with C++17, `std::span` is not available. However, the [Microsoft Guidelines Support Library](https://github.com/Microsoft/GSL) (aka GSL) can be used for its `span` implementation (from which the standard version was defined). The stduuid library defaults to use this implementation if `std::span` is not available. 277 | 278 | To ensure `gsl::span` can be used, make sure the GSL library is available, and the GSL include directory is listed in the include directories for the project. 279 | 280 | If you use cmake to build the test project, make sure the variable called `UUID_USING_CXX20_SPAN` is not defined, or it's value is `OFF` (this is the default value). This will ensure the `gsl` directory will be included in the search list of header directories. 281 | 282 | ## Testing 283 | A testing project is available in the sources. To build and execute the tests do the following: 284 | * Clone or download this repository 285 | * Create a `build` directory in the root directory of the sources 286 | * Run the command `cmake ..` from the `build` directory; if you do not have CMake you must install it first. 287 | * Build the project created in the previous step 288 | * Run the executable. 289 | 290 | **Examples** 291 | 292 | To generate a project files for Visual Studio 2019, you can run the following commands: 293 | ``` 294 | cd build 295 | cmake -G "Visual Studio 17" -A x64 .. 296 | ``` 297 | 298 | To enable the operating system uuid generator set the `UUID_SYSTEM_GENERATOR` variable to `ON`. 299 | ``` 300 | cd build 301 | cmake -G "Visual Studio 17" -A x64 -DUUID_SYSTEM_GENERATOR=ON .. 302 | ``` 303 | 304 | To enable the experimental time-based uuid generator set the `UUID_TIME_GENERATOR` variable to `ON`. 305 | ``` 306 | cd build 307 | cmake -G "Visual Studio 17" -A x64 -DUUID_TIME_GENERATOR=ON .. 308 | ``` 309 | 310 | ## Credits 311 | The SHA1 implementation is based on the [TinySHA1](https://github.com/mohaps/TinySHA1) library. 312 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | 3 | environment: 4 | matrix: 5 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 6 | CMAKE_GENERATOR: Visual Studio 16 2019 7 | CMAKE_GENERATOR_PLATFORM: Win32 8 | 9 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 10 | CMAKE_GENERATOR: Visual Studio 16 2019 11 | CMAKE_GENERATOR_PLATFORM: x64 12 | CMAKE_CLI_FLAGS: -DUUID_SYSTEM_GENERATOR=ON -DUUID_TIME_GENERATOR=ON 13 | 14 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 15 | CMAKE_GENERATOR: Visual Studio 17 2022 16 | CMAKE_GENERATOR_PLATFORM: Win32 17 | 18 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 19 | CMAKE_GENERATOR: Visual Studio 17 2022 20 | CMAKE_GENERATOR_PLATFORM: x64 21 | CMAKE_CLI_FLAGS: -DUUID_SYSTEM_GENERATOR=ON -DUUID_TIME_GENERATOR=ON 22 | 23 | init: 24 | - cmake --version 25 | - msbuild /version 26 | 27 | before_build: 28 | - cmake %CMAKE_CLI_FLAGS% -S . -B build 29 | - cd build 30 | 31 | build_script: 32 | - cmake --build . --config Release 33 | 34 | test_script: 35 | - ctest -C Release -V -j 36 | -------------------------------------------------------------------------------- /catch/catch_reporter_automake.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by Justin R. Wilson on 2/19/2017. 3 | * Copyright 2017 Justin R. Wilson. All rights reserved. 4 | * 5 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | */ 8 | #ifndef TWOBLUECUBES_CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED 9 | #define TWOBLUECUBES_CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED 10 | 11 | // Don't #include any Catch headers here - we can assume they are already 12 | // included before this header. 13 | // This is not good practice in general but is necessary in this case so this 14 | // file can be distributed as a single header that works with the main 15 | // Catch single header. 16 | 17 | namespace Catch { 18 | 19 | struct AutomakeReporter : StreamingReporterBase { 20 | AutomakeReporter( ReporterConfig const& _config ) 21 | : StreamingReporterBase( _config ) 22 | {} 23 | 24 | ~AutomakeReporter() override; 25 | 26 | static std::string getDescription() { 27 | return "Reports test results in the format of Automake .trs files"; 28 | } 29 | 30 | void assertionStarting( AssertionInfo const& ) override {} 31 | 32 | bool assertionEnded( AssertionStats const& /*_assertionStats*/ ) override { return true; } 33 | 34 | void testCaseEnded( TestCaseStats const& _testCaseStats ) override { 35 | // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. 36 | stream << ":test-result: "; 37 | if (_testCaseStats.totals.assertions.allPassed()) { 38 | stream << "PASS"; 39 | } else if (_testCaseStats.totals.assertions.allOk()) { 40 | stream << "XFAIL"; 41 | } else { 42 | stream << "FAIL"; 43 | } 44 | stream << ' ' << _testCaseStats.testInfo.name << '\n'; 45 | StreamingReporterBase::testCaseEnded( _testCaseStats ); 46 | } 47 | 48 | void skipTest( TestCaseInfo const& testInfo ) override { 49 | stream << ":test-result: SKIP " << testInfo.name << '\n'; 50 | } 51 | 52 | }; 53 | 54 | #ifdef CATCH_IMPL 55 | AutomakeReporter::~AutomakeReporter() {} 56 | #endif 57 | 58 | CATCH_REGISTER_REPORTER( "automake", AutomakeReporter) 59 | 60 | } // end namespace Catch 61 | 62 | #endif // TWOBLUECUBES_CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED 63 | -------------------------------------------------------------------------------- /catch/catch_reporter_sonarqube.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by Daniel Garcia on 2018-12-04. 3 | * Copyright Social Point SL. All rights reserved. 4 | * 5 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | */ 8 | #ifndef CATCH_REPORTER_SONARQUBE_HPP_INCLUDED 9 | #define CATCH_REPORTER_SONARQUBE_HPP_INCLUDED 10 | 11 | 12 | // Don't #include any Catch headers here - we can assume they are already 13 | // included before this header. 14 | // This is not good practice in general but is necessary in this case so this 15 | // file can be distributed as a single header that works with the main 16 | // Catch single header. 17 | 18 | #include 19 | 20 | namespace Catch { 21 | 22 | struct SonarQubeReporter : CumulativeReporterBase { 23 | 24 | SonarQubeReporter(ReporterConfig const& config) 25 | : CumulativeReporterBase(config) 26 | , xml(config.stream()) { 27 | m_reporterPrefs.shouldRedirectStdOut = true; 28 | m_reporterPrefs.shouldReportAllAssertions = true; 29 | } 30 | 31 | ~SonarQubeReporter() override; 32 | 33 | static std::string getDescription() { 34 | return "Reports test results in the Generic Test Data SonarQube XML format"; 35 | } 36 | 37 | static std::set getSupportedVerbosities() { 38 | return { Verbosity::Normal }; 39 | } 40 | 41 | void noMatchingTestCases(std::string const& /*spec*/) override {} 42 | 43 | void testRunStarting(TestRunInfo const& testRunInfo) override { 44 | CumulativeReporterBase::testRunStarting(testRunInfo); 45 | xml.startElement("testExecutions"); 46 | xml.writeAttribute("version", "1"); 47 | } 48 | 49 | void testGroupEnded(TestGroupStats const& testGroupStats) override { 50 | CumulativeReporterBase::testGroupEnded(testGroupStats); 51 | writeGroup(*m_testGroups.back()); 52 | } 53 | 54 | void testRunEndedCumulative() override { 55 | xml.endElement(); 56 | } 57 | 58 | void writeGroup(TestGroupNode const& groupNode) { 59 | std::map testsPerFile; 60 | for(auto const& child : groupNode.children) 61 | testsPerFile[child->value.testInfo.lineInfo.file].push_back(child); 62 | 63 | for(auto const& kv : testsPerFile) 64 | writeTestFile(kv.first.c_str(), kv.second); 65 | } 66 | 67 | void writeTestFile(const char* filename, TestGroupNode::ChildNodes const& testCaseNodes) { 68 | XmlWriter::ScopedElement e = xml.scopedElement("file"); 69 | xml.writeAttribute("path", filename); 70 | 71 | for(auto const& child : testCaseNodes) 72 | writeTestCase(*child); 73 | } 74 | 75 | void writeTestCase(TestCaseNode const& testCaseNode) { 76 | // All test cases have exactly one section - which represents the 77 | // test case itself. That section may have 0-n nested sections 78 | assert(testCaseNode.children.size() == 1); 79 | SectionNode const& rootSection = *testCaseNode.children.front(); 80 | writeSection("", rootSection, testCaseNode.value.testInfo.okToFail()); 81 | } 82 | 83 | void writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail) { 84 | std::string name = trim(sectionNode.stats.sectionInfo.name); 85 | if(!rootName.empty()) 86 | name = rootName + '/' + name; 87 | 88 | if(!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) { 89 | XmlWriter::ScopedElement e = xml.scopedElement("testCase"); 90 | xml.writeAttribute("name", name); 91 | xml.writeAttribute("duration", static_cast(sectionNode.stats.durationInSeconds * 1000)); 92 | 93 | writeAssertions(sectionNode, okToFail); 94 | } 95 | 96 | for(auto const& childNode : sectionNode.childSections) 97 | writeSection(name, *childNode, okToFail); 98 | } 99 | 100 | void writeAssertions(SectionNode const& sectionNode, bool okToFail) { 101 | for(auto const& assertion : sectionNode.assertions) 102 | writeAssertion( assertion, okToFail); 103 | } 104 | 105 | void writeAssertion(AssertionStats const& stats, bool okToFail) { 106 | AssertionResult const& result = stats.assertionResult; 107 | if(!result.isOk()) { 108 | std::string elementName; 109 | if(okToFail) { 110 | elementName = "skipped"; 111 | } 112 | else { 113 | switch(result.getResultType()) { 114 | case ResultWas::ThrewException: 115 | case ResultWas::FatalErrorCondition: 116 | elementName = "error"; 117 | break; 118 | case ResultWas::ExplicitFailure: 119 | elementName = "failure"; 120 | break; 121 | case ResultWas::ExpressionFailed: 122 | elementName = "failure"; 123 | break; 124 | case ResultWas::DidntThrowException: 125 | elementName = "failure"; 126 | break; 127 | 128 | // We should never see these here: 129 | case ResultWas::Info: 130 | case ResultWas::Warning: 131 | case ResultWas::Ok: 132 | case ResultWas::Unknown: 133 | case ResultWas::FailureBit: 134 | case ResultWas::Exception: 135 | elementName = "internalError"; 136 | break; 137 | } 138 | } 139 | 140 | XmlWriter::ScopedElement e = xml.scopedElement(elementName); 141 | 142 | ReusableStringStream messageRss; 143 | messageRss << result.getTestMacroName() << "(" << result.getExpression() << ")"; 144 | xml.writeAttribute("message", messageRss.str()); 145 | 146 | ReusableStringStream textRss; 147 | if (stats.totals.assertions.total() > 0) { 148 | textRss << "FAILED:\n"; 149 | if (result.hasExpression()) { 150 | textRss << "\t" << result.getExpressionInMacro() << "\n"; 151 | } 152 | if (result.hasExpandedExpression()) { 153 | textRss << "with expansion:\n\t" << result.getExpandedExpression() << "\n"; 154 | } 155 | } 156 | 157 | if(!result.getMessage().empty()) 158 | textRss << result.getMessage() << "\n"; 159 | 160 | for(auto const& msg : stats.infoMessages) 161 | if(msg.type == ResultWas::Info) 162 | textRss << msg.message << "\n"; 163 | 164 | textRss << "at " << result.getSourceInfo(); 165 | xml.writeText(textRss.str(), XmlFormatting::Newline); 166 | } 167 | } 168 | 169 | private: 170 | XmlWriter xml; 171 | }; 172 | 173 | #ifdef CATCH_IMPL 174 | SonarQubeReporter::~SonarQubeReporter() {} 175 | #endif 176 | 177 | CATCH_REGISTER_REPORTER( "sonarqube", SonarQubeReporter ) 178 | 179 | } // end namespace Catch 180 | 181 | #endif // CATCH_REPORTER_SONARQUBE_HPP_INCLUDED -------------------------------------------------------------------------------- /catch/catch_reporter_tap.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by Colton Wolkins on 2015-08-15. 3 | * Copyright 2015 Martin Moene. All rights reserved. 4 | * 5 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | */ 8 | #ifndef TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED 9 | #define TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED 10 | 11 | 12 | // Don't #include any Catch headers here - we can assume they are already 13 | // included before this header. 14 | // This is not good practice in general but is necessary in this case so this 15 | // file can be distributed as a single header that works with the main 16 | // Catch single header. 17 | 18 | #include 19 | 20 | namespace Catch { 21 | 22 | struct TAPReporter : StreamingReporterBase { 23 | 24 | using StreamingReporterBase::StreamingReporterBase; 25 | 26 | TAPReporter( ReporterConfig const& config ): 27 | StreamingReporterBase( config ) { 28 | m_reporterPrefs.shouldReportAllAssertions = true; 29 | } 30 | 31 | ~TAPReporter() override; 32 | 33 | static std::string getDescription() { 34 | return "Reports test results in TAP format, suitable for test harnesses"; 35 | } 36 | 37 | void noMatchingTestCases( std::string const& spec ) override { 38 | stream << "# No test cases matched '" << spec << "'" << std::endl; 39 | } 40 | 41 | void assertionStarting( AssertionInfo const& ) override {} 42 | 43 | bool assertionEnded( AssertionStats const& _assertionStats ) override { 44 | ++counter; 45 | 46 | stream << "# " << currentTestCaseInfo->name << std::endl; 47 | AssertionPrinter printer( stream, _assertionStats, counter ); 48 | printer.print(); 49 | 50 | stream << std::endl; 51 | return true; 52 | } 53 | 54 | void testRunEnded( TestRunStats const& _testRunStats ) override { 55 | printTotals( _testRunStats.totals ); 56 | stream << "\n" << std::endl; 57 | StreamingReporterBase::testRunEnded( _testRunStats ); 58 | } 59 | 60 | private: 61 | std::size_t counter = 0; 62 | class AssertionPrinter { 63 | public: 64 | AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; 65 | AssertionPrinter( AssertionPrinter const& ) = delete; 66 | AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter ) 67 | : stream( _stream ) 68 | , result( _stats.assertionResult ) 69 | , messages( _stats.infoMessages ) 70 | , itMessage( _stats.infoMessages.begin() ) 71 | , printInfoMessages( true ) 72 | , counter(_counter) 73 | {} 74 | 75 | void print() { 76 | itMessage = messages.begin(); 77 | 78 | switch( result.getResultType() ) { 79 | case ResultWas::Ok: 80 | printResultType( passedString() ); 81 | printOriginalExpression(); 82 | printReconstructedExpression(); 83 | if ( ! result.hasExpression() ) 84 | printRemainingMessages( Colour::None ); 85 | else 86 | printRemainingMessages(); 87 | break; 88 | case ResultWas::ExpressionFailed: 89 | if (result.isOk()) { 90 | printResultType(passedString()); 91 | } else { 92 | printResultType(failedString()); 93 | } 94 | printOriginalExpression(); 95 | printReconstructedExpression(); 96 | if (result.isOk()) { 97 | printIssue(" # TODO"); 98 | } 99 | printRemainingMessages(); 100 | break; 101 | case ResultWas::ThrewException: 102 | printResultType( failedString() ); 103 | printIssue( "unexpected exception with message:" ); 104 | printMessage(); 105 | printExpressionWas(); 106 | printRemainingMessages(); 107 | break; 108 | case ResultWas::FatalErrorCondition: 109 | printResultType( failedString() ); 110 | printIssue( "fatal error condition with message:" ); 111 | printMessage(); 112 | printExpressionWas(); 113 | printRemainingMessages(); 114 | break; 115 | case ResultWas::DidntThrowException: 116 | printResultType( failedString() ); 117 | printIssue( "expected exception, got none" ); 118 | printExpressionWas(); 119 | printRemainingMessages(); 120 | break; 121 | case ResultWas::Info: 122 | printResultType( "info" ); 123 | printMessage(); 124 | printRemainingMessages(); 125 | break; 126 | case ResultWas::Warning: 127 | printResultType( "warning" ); 128 | printMessage(); 129 | printRemainingMessages(); 130 | break; 131 | case ResultWas::ExplicitFailure: 132 | printResultType( failedString() ); 133 | printIssue( "explicitly" ); 134 | printRemainingMessages( Colour::None ); 135 | break; 136 | // These cases are here to prevent compiler warnings 137 | case ResultWas::Unknown: 138 | case ResultWas::FailureBit: 139 | case ResultWas::Exception: 140 | printResultType( "** internal error **" ); 141 | break; 142 | } 143 | } 144 | 145 | private: 146 | static Colour::Code dimColour() { return Colour::FileName; } 147 | 148 | static const char* failedString() { return "not ok"; } 149 | static const char* passedString() { return "ok"; } 150 | 151 | void printSourceInfo() const { 152 | Colour colourGuard( dimColour() ); 153 | stream << result.getSourceInfo() << ":"; 154 | } 155 | 156 | void printResultType( std::string const& passOrFail ) const { 157 | if( !passOrFail.empty() ) { 158 | stream << passOrFail << ' ' << counter << " -"; 159 | } 160 | } 161 | 162 | void printIssue( std::string const& issue ) const { 163 | stream << " " << issue; 164 | } 165 | 166 | void printExpressionWas() { 167 | if( result.hasExpression() ) { 168 | stream << ";"; 169 | { 170 | Colour colour( dimColour() ); 171 | stream << " expression was:"; 172 | } 173 | printOriginalExpression(); 174 | } 175 | } 176 | 177 | void printOriginalExpression() const { 178 | if( result.hasExpression() ) { 179 | stream << " " << result.getExpression(); 180 | } 181 | } 182 | 183 | void printReconstructedExpression() const { 184 | if( result.hasExpandedExpression() ) { 185 | { 186 | Colour colour( dimColour() ); 187 | stream << " for: "; 188 | } 189 | std::string expr = result.getExpandedExpression(); 190 | std::replace( expr.begin(), expr.end(), '\n', ' '); 191 | stream << expr; 192 | } 193 | } 194 | 195 | void printMessage() { 196 | if ( itMessage != messages.end() ) { 197 | stream << " '" << itMessage->message << "'"; 198 | ++itMessage; 199 | } 200 | } 201 | 202 | void printRemainingMessages( Colour::Code colour = dimColour() ) { 203 | if (itMessage == messages.end()) { 204 | return; 205 | } 206 | 207 | const auto itEnd = messages.cend(); 208 | const auto N = static_cast( std::distance( itMessage, itEnd ) ); 209 | 210 | { 211 | Colour colourGuard( colour ); 212 | stream << " with " << pluralise( N, "message" ) << ":"; 213 | } 214 | 215 | while( itMessage != itEnd ) { 216 | // If this assertion is a warning ignore any INFO messages 217 | if( printInfoMessages || itMessage->type != ResultWas::Info ) { 218 | stream << " '" << itMessage->message << "'"; 219 | if ( ++itMessage != itEnd ) { 220 | Colour colourGuard( dimColour() ); 221 | stream << " and"; 222 | } 223 | continue; 224 | } 225 | ++itMessage; 226 | } 227 | } 228 | 229 | private: 230 | std::ostream& stream; 231 | AssertionResult const& result; 232 | std::vector messages; 233 | std::vector::const_iterator itMessage; 234 | bool printInfoMessages; 235 | std::size_t counter; 236 | }; 237 | 238 | void printTotals( const Totals& totals ) const { 239 | stream << "1.." << totals.assertions.total(); 240 | if( totals.testCases.total() == 0 ) { 241 | stream << " # Skipped: No tests ran."; 242 | } 243 | } 244 | }; 245 | 246 | #ifdef CATCH_IMPL 247 | TAPReporter::~TAPReporter() {} 248 | #endif 249 | 250 | CATCH_REGISTER_REPORTER( "tap", TAPReporter ) 251 | 252 | } // end namespace Catch 253 | 254 | #endif // TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED 255 | -------------------------------------------------------------------------------- /catch/catch_reporter_teamcity.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by Phil Nash on 19th December 2014 3 | * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. 4 | * 5 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | */ 8 | #ifndef TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED 9 | #define TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED 10 | 11 | // Don't #include any Catch headers here - we can assume they are already 12 | // included before this header. 13 | // This is not good practice in general but is necessary in this case so this 14 | // file can be distributed as a single header that works with the main 15 | // Catch single header. 16 | 17 | #include 18 | 19 | #ifdef __clang__ 20 | # pragma clang diagnostic push 21 | # pragma clang diagnostic ignored "-Wpadded" 22 | #endif 23 | 24 | namespace Catch { 25 | 26 | struct TeamCityReporter : StreamingReporterBase { 27 | TeamCityReporter( ReporterConfig const& _config ) 28 | : StreamingReporterBase( _config ) 29 | { 30 | m_reporterPrefs.shouldRedirectStdOut = true; 31 | } 32 | 33 | static std::string escape( std::string const& str ) { 34 | std::string escaped = str; 35 | replaceInPlace( escaped, "|", "||" ); 36 | replaceInPlace( escaped, "'", "|'" ); 37 | replaceInPlace( escaped, "\n", "|n" ); 38 | replaceInPlace( escaped, "\r", "|r" ); 39 | replaceInPlace( escaped, "[", "|[" ); 40 | replaceInPlace( escaped, "]", "|]" ); 41 | return escaped; 42 | } 43 | ~TeamCityReporter() override; 44 | 45 | static std::string getDescription() { 46 | return "Reports test results as TeamCity service messages"; 47 | } 48 | 49 | void skipTest( TestCaseInfo const& /* testInfo */ ) override { 50 | } 51 | 52 | void noMatchingTestCases( std::string const& /* spec */ ) override {} 53 | 54 | void testGroupStarting( GroupInfo const& groupInfo ) override { 55 | StreamingReporterBase::testGroupStarting( groupInfo ); 56 | stream << "##teamcity[testSuiteStarted name='" 57 | << escape( groupInfo.name ) << "']\n"; 58 | } 59 | void testGroupEnded( TestGroupStats const& testGroupStats ) override { 60 | StreamingReporterBase::testGroupEnded( testGroupStats ); 61 | stream << "##teamcity[testSuiteFinished name='" 62 | << escape( testGroupStats.groupInfo.name ) << "']\n"; 63 | } 64 | 65 | 66 | void assertionStarting( AssertionInfo const& ) override {} 67 | 68 | bool assertionEnded( AssertionStats const& assertionStats ) override { 69 | AssertionResult const& result = assertionStats.assertionResult; 70 | if( !result.isOk() ) { 71 | 72 | ReusableStringStream msg; 73 | if( !m_headerPrintedForThisSection ) 74 | printSectionHeader( msg.get() ); 75 | m_headerPrintedForThisSection = true; 76 | 77 | msg << result.getSourceInfo() << "\n"; 78 | 79 | switch( result.getResultType() ) { 80 | case ResultWas::ExpressionFailed: 81 | msg << "expression failed"; 82 | break; 83 | case ResultWas::ThrewException: 84 | msg << "unexpected exception"; 85 | break; 86 | case ResultWas::FatalErrorCondition: 87 | msg << "fatal error condition"; 88 | break; 89 | case ResultWas::DidntThrowException: 90 | msg << "no exception was thrown where one was expected"; 91 | break; 92 | case ResultWas::ExplicitFailure: 93 | msg << "explicit failure"; 94 | break; 95 | 96 | // We shouldn't get here because of the isOk() test 97 | case ResultWas::Ok: 98 | case ResultWas::Info: 99 | case ResultWas::Warning: 100 | CATCH_ERROR( "Internal error in TeamCity reporter" ); 101 | // These cases are here to prevent compiler warnings 102 | case ResultWas::Unknown: 103 | case ResultWas::FailureBit: 104 | case ResultWas::Exception: 105 | CATCH_ERROR( "Not implemented" ); 106 | } 107 | if( assertionStats.infoMessages.size() == 1 ) 108 | msg << " with message:"; 109 | if( assertionStats.infoMessages.size() > 1 ) 110 | msg << " with messages:"; 111 | for( auto const& messageInfo : assertionStats.infoMessages ) 112 | msg << "\n \"" << messageInfo.message << "\""; 113 | 114 | 115 | if( result.hasExpression() ) { 116 | msg << 117 | "\n " << result.getExpressionInMacro() << "\n" 118 | "with expansion:\n" << 119 | " " << result.getExpandedExpression() << "\n"; 120 | } 121 | 122 | if( currentTestCaseInfo->okToFail() ) { 123 | msg << "- failure ignore as test marked as 'ok to fail'\n"; 124 | stream << "##teamcity[testIgnored" 125 | << " name='" << escape( currentTestCaseInfo->name )<< "'" 126 | << " message='" << escape( msg.str() ) << "'" 127 | << "]\n"; 128 | } 129 | else { 130 | stream << "##teamcity[testFailed" 131 | << " name='" << escape( currentTestCaseInfo->name )<< "'" 132 | << " message='" << escape( msg.str() ) << "'" 133 | << "]\n"; 134 | } 135 | } 136 | stream.flush(); 137 | return true; 138 | } 139 | 140 | void sectionStarting( SectionInfo const& sectionInfo ) override { 141 | m_headerPrintedForThisSection = false; 142 | StreamingReporterBase::sectionStarting( sectionInfo ); 143 | } 144 | 145 | void testCaseStarting( TestCaseInfo const& testInfo ) override { 146 | m_testTimer.start(); 147 | StreamingReporterBase::testCaseStarting( testInfo ); 148 | stream << "##teamcity[testStarted name='" 149 | << escape( testInfo.name ) << "']\n"; 150 | stream.flush(); 151 | } 152 | 153 | void testCaseEnded( TestCaseStats const& testCaseStats ) override { 154 | StreamingReporterBase::testCaseEnded( testCaseStats ); 155 | if( !testCaseStats.stdOut.empty() ) 156 | stream << "##teamcity[testStdOut name='" 157 | << escape( testCaseStats.testInfo.name ) 158 | << "' out='" << escape( testCaseStats.stdOut ) << "']\n"; 159 | if( !testCaseStats.stdErr.empty() ) 160 | stream << "##teamcity[testStdErr name='" 161 | << escape( testCaseStats.testInfo.name ) 162 | << "' out='" << escape( testCaseStats.stdErr ) << "']\n"; 163 | stream << "##teamcity[testFinished name='" 164 | << escape( testCaseStats.testInfo.name ) << "' duration='" 165 | << m_testTimer.getElapsedMilliseconds() << "']\n"; 166 | stream.flush(); 167 | } 168 | 169 | private: 170 | void printSectionHeader( std::ostream& os ) { 171 | assert( !m_sectionStack.empty() ); 172 | 173 | if( m_sectionStack.size() > 1 ) { 174 | os << getLineOfChars<'-'>() << "\n"; 175 | 176 | std::vector::const_iterator 177 | it = m_sectionStack.begin()+1, // Skip first section (test case) 178 | itEnd = m_sectionStack.end(); 179 | for( ; it != itEnd; ++it ) 180 | printHeaderString( os, it->name ); 181 | os << getLineOfChars<'-'>() << "\n"; 182 | } 183 | 184 | SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; 185 | 186 | os << lineInfo << "\n"; 187 | os << getLineOfChars<'.'>() << "\n\n"; 188 | } 189 | 190 | // if string has a : in first line will set indent to follow it on 191 | // subsequent lines 192 | static void printHeaderString( std::ostream& os, std::string const& _string, std::size_t indent = 0 ) { 193 | std::size_t i = _string.find( ": " ); 194 | if( i != std::string::npos ) 195 | i+=2; 196 | else 197 | i = 0; 198 | os << Column( _string ) 199 | .indent( indent+i) 200 | .initialIndent( indent ) << "\n"; 201 | } 202 | private: 203 | bool m_headerPrintedForThisSection = false; 204 | Timer m_testTimer; 205 | }; 206 | 207 | #ifdef CATCH_IMPL 208 | TeamCityReporter::~TeamCityReporter() {} 209 | #endif 210 | 211 | CATCH_REGISTER_REPORTER( "teamcity", TeamCityReporter ) 212 | 213 | } // end namespace Catch 214 | 215 | #ifdef __clang__ 216 | # pragma clang diagnostic pop 217 | #endif 218 | 219 | #endif // TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED 220 | -------------------------------------------------------------------------------- /cmake/Config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(CMakeFindDependencyMacro) 4 | if (@UUID_SYSTEM_GENERATOR@) 5 | if (WIN32 OR APPLE) 6 | else () 7 | list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) 8 | find_dependency(Libuuid REQUIRED) 9 | endif () 10 | endif () 11 | 12 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake") 13 | 14 | check_required_components(@PROJECT_NAME@) -------------------------------------------------------------------------------- /cmake/FindLibuuid.cmake: -------------------------------------------------------------------------------- 1 | find_path(Libuuid_INCLUDE_DIRS uuid/uuid.h) 2 | find_library(Libuuid_LIBRARIES uuid) 3 | 4 | if (Libuuid_LIBRARIES AND Libuuid_INCLUDE_DIRS) 5 | set(Libuuid_FOUND YES) 6 | if (NOT Libuuid_FIND_QUIETLY) 7 | message(STATUS "Found libuuid: ${Libuuid_LIBRARIES}") 8 | endif () 9 | else () 10 | if (Libuuid_FIND_REQUIRED) 11 | message(SEND_ERROR "Could NOT find libuuid") 12 | else () 13 | if (NOT Libuuid_FIND_QUIETLY) 14 | message(STATUS "Could NOT find libuuid") 15 | endif () 16 | endif () 17 | endif () -------------------------------------------------------------------------------- /gsl/gsl: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4 | // 5 | // This code is licensed under the MIT License (MIT). 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13 | // THE SOFTWARE. 14 | // 15 | /////////////////////////////////////////////////////////////////////////////// 16 | 17 | #ifndef GSL_GSL_H 18 | #define GSL_GSL_H 19 | 20 | #include "gsl_algorithm" // copy 21 | #include "gsl_assert" // Ensures/Expects 22 | #include "gsl_byte" // byte 23 | #include "gsl_util" // finally()/narrow()/narrow_cast()... 24 | #include "multi_span" // multi_span, strided_span... 25 | #include "pointers" // owner, not_null 26 | #include "span" // span 27 | #include "string_span" // zstring, string_span, zstring_builder... 28 | 29 | #endif // GSL_GSL_H 30 | -------------------------------------------------------------------------------- /gsl/gsl_algorithm: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4 | // 5 | // This code is licensed under the MIT License (MIT). 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13 | // THE SOFTWARE. 14 | // 15 | /////////////////////////////////////////////////////////////////////////////// 16 | 17 | #ifndef GSL_ALGORITHM_H 18 | #define GSL_ALGORITHM_H 19 | 20 | #include "gsl_assert" // for Expects 21 | #include "span" // for dynamic_extent, span 22 | 23 | #include // for copy_n 24 | #include // for ptrdiff_t 25 | #include // for is_assignable 26 | 27 | #ifdef _MSC_VER 28 | #pragma warning(push) 29 | 30 | // turn off some warnings that are noisy about our Expects statements 31 | #pragma warning(disable : 4127) // conditional expression is constant 32 | #pragma warning(disable : 4996) // unsafe use of std::copy_n 33 | 34 | // blanket turn off warnings from CppCoreCheck for now 35 | // so people aren't annoyed by them when running the tool. 36 | // more targeted suppressions will be added in a future update to the GSL 37 | #pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) 38 | #endif // _MSC_VER 39 | 40 | namespace gsl 41 | { 42 | 43 | template 45 | void copy(span src, span dest) 46 | { 47 | static_assert(std::is_assignable::value, 48 | "Elements of source span can not be assigned to elements of destination span"); 49 | static_assert(SrcExtent == dynamic_extent || DestExtent == dynamic_extent || 50 | (SrcExtent <= DestExtent), 51 | "Source range is longer than target range"); 52 | 53 | Expects(dest.size() >= src.size()); 54 | std::copy_n(src.data(), src.size(), dest.data()); 55 | } 56 | 57 | } // namespace gsl 58 | 59 | #ifdef _MSC_VER 60 | #pragma warning(pop) 61 | #endif // _MSC_VER 62 | 63 | #endif // GSL_ALGORITHM_H 64 | -------------------------------------------------------------------------------- /gsl/gsl_assert: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4 | // 5 | // This code is licensed under the MIT License (MIT). 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13 | // THE SOFTWARE. 14 | // 15 | /////////////////////////////////////////////////////////////////////////////// 16 | 17 | #ifndef GSL_CONTRACTS_H 18 | #define GSL_CONTRACTS_H 19 | 20 | #include 21 | #include // for logic_error 22 | 23 | // 24 | // Temporary until MSVC STL supports no-exceptions mode. 25 | // Currently terminate is a no-op in this mode, so we add termination behavior back 26 | // 27 | #if defined(_MSC_VER) && defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS 28 | #define GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND 29 | #endif 30 | 31 | // 32 | // There are three configuration options for this GSL implementation's behavior 33 | // when pre/post conditions on the GSL types are violated: 34 | // 35 | // 1. GSL_TERMINATE_ON_CONTRACT_VIOLATION: std::terminate will be called (default) 36 | // 2. GSL_THROW_ON_CONTRACT_VIOLATION: a gsl::fail_fast exception will be thrown 37 | // 3. GSL_UNENFORCED_ON_CONTRACT_VIOLATION: nothing happens 38 | // 39 | #if !(defined(GSL_THROW_ON_CONTRACT_VIOLATION) || defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) || \ 40 | defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION)) 41 | #define GSL_TERMINATE_ON_CONTRACT_VIOLATION 42 | #endif 43 | 44 | #define GSL_STRINGIFY_DETAIL(x) #x 45 | #define GSL_STRINGIFY(x) GSL_STRINGIFY_DETAIL(x) 46 | 47 | #if defined(__clang__) || defined(__GNUC__) 48 | #define GSL_LIKELY(x) __builtin_expect(!!(x), 1) 49 | #define GSL_UNLIKELY(x) __builtin_expect(!!(x), 0) 50 | #else 51 | #define GSL_LIKELY(x) (!!(x)) 52 | #define GSL_UNLIKELY(x) (!!(x)) 53 | #endif 54 | 55 | // 56 | // GSL_ASSUME(cond) 57 | // 58 | // Tell the optimizer that the predicate cond must hold. It is unspecified 59 | // whether or not cond is actually evaluated. 60 | // 61 | #ifdef _MSC_VER 62 | #define GSL_ASSUME(cond) __assume(cond) 63 | #elif defined(__GNUC__) 64 | #define GSL_ASSUME(cond) ((cond) ? static_cast(0) : __builtin_unreachable()) 65 | #else 66 | #define GSL_ASSUME(cond) static_cast((cond) ? 0 : 0) 67 | #endif 68 | 69 | // 70 | // GSL.assert: assertions 71 | // 72 | 73 | namespace gsl 74 | { 75 | struct fail_fast : public std::logic_error 76 | { 77 | explicit fail_fast(char const* const message) : std::logic_error(message) {} 78 | }; 79 | 80 | namespace details 81 | { 82 | #if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) 83 | 84 | typedef void (__cdecl *terminate_handler)(); 85 | 86 | inline gsl::details::terminate_handler& get_terminate_handler() noexcept 87 | { 88 | static terminate_handler handler = &abort; 89 | return handler; 90 | } 91 | 92 | #endif 93 | 94 | [[noreturn]] inline void terminate() noexcept 95 | { 96 | #if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) 97 | (*gsl::details::get_terminate_handler())(); 98 | #else 99 | std::terminate(); 100 | #endif 101 | } 102 | 103 | #if defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) 104 | 105 | template 106 | [[noreturn]] void throw_exception(Exception&&) 107 | { 108 | gsl::details::terminate(); 109 | } 110 | 111 | #else 112 | 113 | template 114 | [[noreturn]] void throw_exception(Exception&& exception) 115 | { 116 | throw std::forward(exception); 117 | } 118 | 119 | #endif 120 | 121 | } // namespace details 122 | } // namespace gsl 123 | 124 | #if defined(GSL_THROW_ON_CONTRACT_VIOLATION) 125 | 126 | #define GSL_CONTRACT_CHECK(type, cond) \ 127 | (GSL_LIKELY(cond) ? static_cast(0) \ 128 | : gsl::details::throw_exception(gsl::fail_fast( \ 129 | "GSL: " type " failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__)))) 130 | 131 | #elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) 132 | 133 | #define GSL_CONTRACT_CHECK(type, cond) \ 134 | (GSL_LIKELY(cond) ? static_cast(0) : gsl::details::terminate()) 135 | 136 | #elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION) 137 | 138 | #define GSL_CONTRACT_CHECK(type, cond) GSL_ASSUME(cond) 139 | 140 | #endif 141 | 142 | #define Expects(cond) GSL_CONTRACT_CHECK("Precondition", cond) 143 | #define Ensures(cond) GSL_CONTRACT_CHECK("Postcondition", cond) 144 | 145 | #endif // GSL_CONTRACTS_H 146 | -------------------------------------------------------------------------------- /gsl/gsl_byte: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4 | // 5 | // This code is licensed under the MIT License (MIT). 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13 | // THE SOFTWARE. 14 | // 15 | /////////////////////////////////////////////////////////////////////////////// 16 | 17 | #ifndef GSL_BYTE_H 18 | #define GSL_BYTE_H 19 | 20 | #include 21 | 22 | #ifdef _MSC_VER 23 | 24 | #pragma warning(push) 25 | 26 | // don't warn about function style casts in byte related operators 27 | #pragma warning(disable : 26493) 28 | 29 | #ifndef GSL_USE_STD_BYTE 30 | // this tests if we are under MSVC and the standard lib has std::byte and it is enabled 31 | #if defined(_HAS_STD_BYTE) && _HAS_STD_BYTE 32 | 33 | #define GSL_USE_STD_BYTE 1 34 | 35 | #else // defined(_HAS_STD_BYTE) && _HAS_STD_BYTE 36 | 37 | #define GSL_USE_STD_BYTE 0 38 | 39 | #endif // defined(_HAS_STD_BYTE) && _HAS_STD_BYTE 40 | #endif // GSL_USE_STD_BYTE 41 | 42 | #else // _MSC_VER 43 | 44 | #ifndef GSL_USE_STD_BYTE 45 | // this tests if we are under GCC or Clang with enough -std:c++1z power to get us std::byte 46 | #if defined(__cplusplus) && (__cplusplus >= 201703L) 47 | 48 | #define GSL_USE_STD_BYTE 1 49 | #include 50 | 51 | #else // defined(__cplusplus) && (__cplusplus >= 201703L) 52 | 53 | #define GSL_USE_STD_BYTE 0 54 | 55 | #endif //defined(__cplusplus) && (__cplusplus >= 201703L) 56 | #endif // GSL_USE_STD_BYTE 57 | 58 | #endif // _MSC_VER 59 | 60 | // Use __may_alias__ attribute on gcc and clang 61 | #if defined __clang__ || (__GNUC__ > 5) 62 | #define byte_may_alias __attribute__((__may_alias__)) 63 | #else // defined __clang__ || defined __GNUC__ 64 | #define byte_may_alias 65 | #endif // defined __clang__ || defined __GNUC__ 66 | 67 | namespace gsl 68 | { 69 | #if GSL_USE_STD_BYTE 70 | 71 | 72 | using std::byte; 73 | using std::to_integer; 74 | 75 | #else // GSL_USE_STD_BYTE 76 | 77 | // This is a simple definition for now that allows 78 | // use of byte within span<> to be standards-compliant 79 | enum class byte_may_alias byte : unsigned char 80 | { 81 | }; 82 | 83 | template ::value>> 84 | constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept 85 | { 86 | return b = byte(static_cast(b) << shift); 87 | } 88 | 89 | template ::value>> 90 | constexpr byte operator<<(byte b, IntegerType shift) noexcept 91 | { 92 | return byte(static_cast(b) << shift); 93 | } 94 | 95 | template ::value>> 96 | constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept 97 | { 98 | return b = byte(static_cast(b) >> shift); 99 | } 100 | 101 | template ::value>> 102 | constexpr byte operator>>(byte b, IntegerType shift) noexcept 103 | { 104 | return byte(static_cast(b) >> shift); 105 | } 106 | 107 | constexpr byte& operator|=(byte& l, byte r) noexcept 108 | { 109 | return l = byte(static_cast(l) | static_cast(r)); 110 | } 111 | 112 | constexpr byte operator|(byte l, byte r) noexcept 113 | { 114 | return byte(static_cast(l) | static_cast(r)); 115 | } 116 | 117 | constexpr byte& operator&=(byte& l, byte r) noexcept 118 | { 119 | return l = byte(static_cast(l) & static_cast(r)); 120 | } 121 | 122 | constexpr byte operator&(byte l, byte r) noexcept 123 | { 124 | return byte(static_cast(l) & static_cast(r)); 125 | } 126 | 127 | constexpr byte& operator^=(byte& l, byte r) noexcept 128 | { 129 | return l = byte(static_cast(l) ^ static_cast(r)); 130 | } 131 | 132 | constexpr byte operator^(byte l, byte r) noexcept 133 | { 134 | return byte(static_cast(l) ^ static_cast(r)); 135 | } 136 | 137 | constexpr byte operator~(byte b) noexcept { return byte(~static_cast(b)); } 138 | 139 | template ::value>> 140 | constexpr IntegerType to_integer(byte b) noexcept 141 | { 142 | return static_cast(b); 143 | } 144 | 145 | #endif // GSL_USE_STD_BYTE 146 | 147 | template 148 | constexpr byte to_byte_impl(T t) noexcept 149 | { 150 | static_assert( 151 | E, "gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. " 152 | "If you are calling to_byte with an integer contant use: gsl::to_byte() version."); 153 | return static_cast(t); 154 | } 155 | template <> 156 | constexpr byte to_byte_impl(unsigned char t) noexcept 157 | { 158 | return byte(t); 159 | } 160 | 161 | template 162 | constexpr byte to_byte(T t) noexcept 163 | { 164 | return to_byte_impl::value, T>(t); 165 | } 166 | 167 | template 168 | constexpr byte to_byte() noexcept 169 | { 170 | static_assert(I >= 0 && I <= 255, 171 | "gsl::byte only has 8 bits of storage, values must be in range 0-255"); 172 | return static_cast(I); 173 | } 174 | 175 | } // namespace gsl 176 | 177 | #ifdef _MSC_VER 178 | #pragma warning(pop) 179 | #endif // _MSC_VER 180 | 181 | #endif // GSL_BYTE_H 182 | -------------------------------------------------------------------------------- /gsl/gsl_util: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4 | // 5 | // This code is licensed under the MIT License (MIT). 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13 | // THE SOFTWARE. 14 | // 15 | /////////////////////////////////////////////////////////////////////////////// 16 | 17 | #ifndef GSL_UTIL_H 18 | #define GSL_UTIL_H 19 | 20 | #include "gsl_assert" // for Expects 21 | 22 | #include 23 | #include // for ptrdiff_t, size_t 24 | #include // for exception 25 | #include // for initializer_list 26 | #include // for is_signed, integral_constant 27 | #include // for forward 28 | 29 | #if defined(_MSC_VER) 30 | 31 | #pragma warning(push) 32 | #pragma warning(disable : 4127) // conditional expression is constant 33 | 34 | #if _MSC_VER < 1910 35 | #pragma push_macro("constexpr") 36 | #define constexpr /*constexpr*/ 37 | #endif // _MSC_VER < 1910 38 | #endif // _MSC_VER 39 | 40 | namespace gsl 41 | { 42 | // 43 | // GSL.util: utilities 44 | // 45 | 46 | // index type for all container indexes/subscripts/sizes 47 | using index = std::ptrdiff_t; 48 | 49 | // final_action allows you to ensure something gets run at the end of a scope 50 | template 51 | class final_action 52 | { 53 | public: 54 | explicit final_action(F f) noexcept : f_(std::move(f)) {} 55 | 56 | final_action(final_action&& other) noexcept : f_(std::move(other.f_)), invoke_(other.invoke_) 57 | { 58 | other.invoke_ = false; 59 | } 60 | 61 | final_action(const final_action&) = delete; 62 | final_action& operator=(const final_action&) = delete; 63 | final_action& operator=(final_action&&) = delete; 64 | 65 | ~final_action() noexcept 66 | { 67 | if (invoke_) f_(); 68 | } 69 | 70 | private: 71 | F f_; 72 | bool invoke_ {true}; 73 | }; 74 | 75 | // finally() - convenience function to generate a final_action 76 | template 77 | 78 | final_action finally(const F& f) noexcept 79 | { 80 | return final_action(f); 81 | } 82 | 83 | template 84 | final_action finally(F&& f) noexcept 85 | { 86 | return final_action(std::forward(f)); 87 | } 88 | 89 | // narrow_cast(): a searchable way to do narrowing casts of values 90 | template 91 | constexpr T narrow_cast(U&& u) noexcept 92 | { 93 | return static_cast(std::forward(u)); 94 | } 95 | 96 | struct narrowing_error : public std::exception 97 | { 98 | }; 99 | 100 | namespace details 101 | { 102 | template 103 | struct is_same_signedness 104 | : public std::integral_constant::value == std::is_signed::value> 105 | { 106 | }; 107 | } 108 | 109 | // narrow() : a checked version of narrow_cast() that throws if the cast changed the value 110 | template 111 | T narrow(U u) 112 | { 113 | T t = narrow_cast(u); 114 | if (static_cast(t) != u) gsl::details::throw_exception(narrowing_error()); 115 | if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) 116 | gsl::details::throw_exception(narrowing_error()); 117 | return t; 118 | } 119 | 120 | // 121 | // at() - Bounds-checked way of accessing builtin arrays, std::array, std::vector 122 | // 123 | template 124 | constexpr T& at(T (&arr)[N], const index i) 125 | { 126 | Expects(i >= 0 && i < narrow_cast(N)); 127 | return arr[static_cast(i)]; 128 | } 129 | 130 | template 131 | constexpr auto at(Cont& cont, const index i) -> decltype(cont[cont.size()]) 132 | { 133 | Expects(i >= 0 && i < narrow_cast(cont.size())); 134 | using size_type = decltype(cont.size()); 135 | return cont[static_cast(i)]; 136 | } 137 | 138 | template 139 | constexpr T at(const std::initializer_list cont, const index i) 140 | { 141 | Expects(i >= 0 && i < narrow_cast(cont.size())); 142 | return *(cont.begin() + i); 143 | } 144 | 145 | } // namespace gsl 146 | 147 | #if defined(_MSC_VER) 148 | #if _MSC_VER < 1910 149 | #undef constexpr 150 | #pragma pop_macro("constexpr") 151 | 152 | #endif // _MSC_VER < 1910 153 | 154 | #pragma warning(pop) 155 | 156 | #endif // _MSC_VER 157 | 158 | #endif // GSL_UTIL_H 159 | -------------------------------------------------------------------------------- /gsl/pointers: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4 | // 5 | // This code is licensed under the MIT License (MIT). 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13 | // THE SOFTWARE. 14 | // 15 | /////////////////////////////////////////////////////////////////////////////// 16 | 17 | #ifndef GSL_POINTERS_H 18 | #define GSL_POINTERS_H 19 | 20 | #include "gsl_assert" // for Ensures, Expects 21 | 22 | #include // for forward 23 | #include // for ptrdiff_t, nullptr_t, ostream, size_t 24 | #include // for shared_ptr, unique_ptr 25 | #include // for hash 26 | #include // for enable_if_t, is_convertible, is_assignable 27 | 28 | #if defined(_MSC_VER) && _MSC_VER < 1910 29 | #pragma push_macro("constexpr") 30 | #define constexpr /*constexpr*/ 31 | 32 | #endif // defined(_MSC_VER) && _MSC_VER < 1910 33 | 34 | namespace gsl 35 | { 36 | 37 | // 38 | // GSL.owner: ownership pointers 39 | // 40 | using std::unique_ptr; 41 | using std::shared_ptr; 42 | 43 | // 44 | // owner 45 | // 46 | // owner is designed as a bridge for code that must deal directly with owning pointers for some reason 47 | // 48 | // T must be a pointer type 49 | // - disallow construction from any type other than pointer type 50 | // 51 | template ::value>> 52 | using owner = T; 53 | 54 | // 55 | // not_null 56 | // 57 | // Restricts a pointer or smart pointer to only hold non-null values. 58 | // 59 | // Has zero size overhead over T. 60 | // 61 | // If T is a pointer (i.e. T == U*) then 62 | // - allow construction from U* 63 | // - disallow construction from nullptr_t 64 | // - disallow default construction 65 | // - ensure construction from null U* fails 66 | // - allow implicit conversion to U* 67 | // 68 | template 69 | class not_null 70 | { 71 | public: 72 | static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); 73 | 74 | template ::value>> 75 | constexpr explicit not_null(U&& u) : ptr_(std::forward(u)) 76 | { 77 | Expects(ptr_ != nullptr); 78 | } 79 | 80 | template ::value>> 81 | constexpr explicit not_null(T u) : ptr_(u) 82 | { 83 | Expects(ptr_ != nullptr); 84 | } 85 | 86 | template ::value>> 87 | constexpr not_null(const not_null& other) : not_null(other.get()) 88 | { 89 | } 90 | 91 | not_null(not_null&& other) = default; 92 | not_null(const not_null& other) = default; 93 | not_null& operator=(const not_null& other) = default; 94 | 95 | constexpr T get() const 96 | { 97 | Ensures(ptr_ != nullptr); 98 | return ptr_; 99 | } 100 | 101 | constexpr operator T() const { return get(); } 102 | constexpr T operator->() const { return get(); } 103 | constexpr decltype(auto) operator*() const { return *get(); } 104 | 105 | // prevents compilation when someone attempts to assign a null pointer constant 106 | not_null(std::nullptr_t) = delete; 107 | not_null& operator=(std::nullptr_t) = delete; 108 | 109 | // unwanted operators...pointers only point to single objects! 110 | not_null& operator++() = delete; 111 | not_null& operator--() = delete; 112 | not_null operator++(int) = delete; 113 | not_null operator--(int) = delete; 114 | not_null& operator+=(std::ptrdiff_t) = delete; 115 | not_null& operator-=(std::ptrdiff_t) = delete; 116 | void operator[](std::ptrdiff_t) const = delete; 117 | 118 | private: 119 | T ptr_; 120 | }; 121 | 122 | template 123 | std::ostream& operator<<(std::ostream& os, const not_null& val) 124 | { 125 | os << val.get(); 126 | return os; 127 | } 128 | 129 | template 130 | auto operator==(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() == rhs.get()) 131 | { 132 | return lhs.get() == rhs.get(); 133 | } 134 | 135 | template 136 | auto operator!=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() != rhs.get()) 137 | { 138 | return lhs.get() != rhs.get(); 139 | } 140 | 141 | template 142 | auto operator<(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() < rhs.get()) 143 | { 144 | return lhs.get() < rhs.get(); 145 | } 146 | 147 | template 148 | auto operator<=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() <= rhs.get()) 149 | { 150 | return lhs.get() <= rhs.get(); 151 | } 152 | 153 | template 154 | auto operator>(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() > rhs.get()) 155 | { 156 | return lhs.get() > rhs.get(); 157 | } 158 | 159 | template 160 | auto operator>=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() >= rhs.get()) 161 | { 162 | return lhs.get() >= rhs.get(); 163 | } 164 | 165 | // more unwanted operators 166 | template 167 | std::ptrdiff_t operator-(const not_null&, const not_null&) = delete; 168 | template 169 | not_null operator-(const not_null&, std::ptrdiff_t) = delete; 170 | template 171 | not_null operator+(const not_null&, std::ptrdiff_t) = delete; 172 | template 173 | not_null operator+(std::ptrdiff_t, const not_null&) = delete; 174 | 175 | } // namespace gsl 176 | 177 | namespace std 178 | { 179 | template 180 | struct hash> 181 | { 182 | std::size_t operator()(const gsl::not_null& value) const { return hash{}(value); } 183 | }; 184 | 185 | } // namespace std 186 | 187 | #if defined(_MSC_VER) && _MSC_VER < 1910 188 | #undef constexpr 189 | #pragma pop_macro("constexpr") 190 | 191 | #endif // defined(_MSC_VER) && _MSC_VER < 1910 192 | 193 | #endif // GSL_POINTERS_H 194 | -------------------------------------------------------------------------------- /gsl/span: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4 | // 5 | // This code is licensed under the MIT License (MIT). 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13 | // THE SOFTWARE. 14 | // 15 | /////////////////////////////////////////////////////////////////////////////// 16 | 17 | #ifndef GSL_SPAN_H 18 | #define GSL_SPAN_H 19 | 20 | #include "gsl_assert" // for Expects 21 | #include "gsl_byte" // for byte 22 | #include "gsl_util" // for narrow_cast, narrow 23 | 24 | #include // for lexicographical_compare 25 | #include // for array 26 | #include // for ptrdiff_t, size_t, nullptr_t 27 | #include // for reverse_iterator, distance, random_access_... 28 | #include 29 | #include 30 | #include // for enable_if_t, declval, is_convertible, inte... 31 | #include 32 | 33 | #ifdef _MSC_VER 34 | #pragma warning(push) 35 | 36 | // turn off some warnings that are noisy about our Expects statements 37 | #pragma warning(disable : 4127) // conditional expression is constant 38 | #pragma warning(disable : 4702) // unreachable code 39 | 40 | // blanket turn off warnings from CppCoreCheck for now 41 | // so people aren't annoyed by them when running the tool. 42 | // more targeted suppressions will be added in a future update to the GSL 43 | #pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) 44 | 45 | #if _MSC_VER < 1910 46 | #pragma push_macro("constexpr") 47 | #define constexpr /*constexpr*/ 48 | #define GSL_USE_STATIC_CONSTEXPR_WORKAROUND 49 | 50 | #endif // _MSC_VER < 1910 51 | #else // _MSC_VER 52 | 53 | // See if we have enough C++17 power to use a static constexpr data member 54 | // without needing an out-of-line definition 55 | #if !(defined(__cplusplus) && (__cplusplus >= 201703L)) 56 | #define GSL_USE_STATIC_CONSTEXPR_WORKAROUND 57 | #endif // !(defined(__cplusplus) && (__cplusplus >= 201703L)) 58 | 59 | #endif // _MSC_VER 60 | 61 | // GCC 7 does not like the signed unsigned missmatch (size_t ptrdiff_t) 62 | // While there is a conversion from signed to unsigned, it happens at 63 | // compiletime, so the compiler wouldn't have to warn indiscriminently, but 64 | // could check if the source value actually doesn't fit into the target type 65 | // and only warn in those cases. 66 | #if __GNUC__ > 6 67 | #pragma GCC diagnostic push 68 | #pragma GCC diagnostic ignored "-Wsign-conversion" 69 | #endif 70 | 71 | namespace gsl 72 | { 73 | 74 | // [views.constants], constants 75 | constexpr const std::ptrdiff_t dynamic_extent = -1; 76 | 77 | template 78 | class span; 79 | 80 | // implementation details 81 | namespace details 82 | { 83 | template 84 | struct is_span_oracle : std::false_type 85 | { 86 | }; 87 | 88 | template 89 | struct is_span_oracle> : std::true_type 90 | { 91 | }; 92 | 93 | template 94 | struct is_span : public is_span_oracle> 95 | { 96 | }; 97 | 98 | template 99 | struct is_std_array_oracle : std::false_type 100 | { 101 | }; 102 | 103 | template 104 | struct is_std_array_oracle> : std::true_type 105 | { 106 | }; 107 | 108 | template 109 | struct is_std_array : public is_std_array_oracle> 110 | { 111 | }; 112 | 113 | template 114 | struct is_allowed_extent_conversion 115 | : public std::integral_constant 117 | { 118 | }; 119 | 120 | template 121 | struct is_allowed_element_type_conversion 122 | : public std::integral_constant::value> 123 | { 124 | }; 125 | 126 | template 127 | class span_iterator 128 | { 129 | using element_type_ = typename Span::element_type; 130 | 131 | public: 132 | 133 | #ifdef _MSC_VER 134 | // Tell Microsoft standard library that span_iterators are checked. 135 | using _Unchecked_type = typename Span::pointer; 136 | #endif 137 | 138 | using iterator_category = std::random_access_iterator_tag; 139 | using value_type = std::remove_cv_t; 140 | using difference_type = typename Span::index_type; 141 | 142 | using reference = std::conditional_t&; 143 | using pointer = std::add_pointer_t; 144 | 145 | span_iterator() = default; 146 | 147 | constexpr span_iterator(const Span* span, typename Span::index_type idx) noexcept 148 | : span_(span), index_(idx) 149 | {} 150 | 151 | friend span_iterator; 152 | template* = nullptr> 153 | constexpr span_iterator(const span_iterator& other) noexcept 154 | : span_iterator(other.span_, other.index_) 155 | { 156 | } 157 | 158 | constexpr reference operator*() const 159 | { 160 | Expects(index_ != span_->size()); 161 | return *(span_->data() + index_); 162 | } 163 | 164 | constexpr pointer operator->() const 165 | { 166 | Expects(index_ != span_->size()); 167 | return span_->data() + index_; 168 | } 169 | 170 | constexpr span_iterator& operator++() 171 | { 172 | Expects(0 <= index_ && index_ != span_->size()); 173 | ++index_; 174 | return *this; 175 | } 176 | 177 | constexpr span_iterator operator++(int) 178 | { 179 | auto ret = *this; 180 | ++(*this); 181 | return ret; 182 | } 183 | 184 | constexpr span_iterator& operator--() 185 | { 186 | Expects(index_ != 0 && index_ <= span_->size()); 187 | --index_; 188 | return *this; 189 | } 190 | 191 | constexpr span_iterator operator--(int) 192 | { 193 | auto ret = *this; 194 | --(*this); 195 | return ret; 196 | } 197 | 198 | constexpr span_iterator operator+(difference_type n) const 199 | { 200 | auto ret = *this; 201 | return ret += n; 202 | } 203 | 204 | friend constexpr span_iterator operator+(difference_type n, span_iterator const& rhs) 205 | { 206 | return rhs + n; 207 | } 208 | 209 | constexpr span_iterator& operator+=(difference_type n) 210 | { 211 | Expects((index_ + n) >= 0 && (index_ + n) <= span_->size()); 212 | index_ += n; 213 | return *this; 214 | } 215 | 216 | constexpr span_iterator operator-(difference_type n) const 217 | { 218 | auto ret = *this; 219 | return ret -= n; 220 | } 221 | 222 | constexpr span_iterator& operator-=(difference_type n) { return *this += -n; } 223 | 224 | constexpr difference_type operator-(span_iterator rhs) const 225 | { 226 | Expects(span_ == rhs.span_); 227 | return index_ - rhs.index_; 228 | } 229 | 230 | constexpr reference operator[](difference_type n) const 231 | { 232 | return *(*this + n); 233 | } 234 | 235 | constexpr friend bool operator==(span_iterator lhs, 236 | span_iterator rhs) noexcept 237 | { 238 | return lhs.span_ == rhs.span_ && lhs.index_ == rhs.index_; 239 | } 240 | 241 | constexpr friend bool operator!=(span_iterator lhs, 242 | span_iterator rhs) noexcept 243 | { 244 | return !(lhs == rhs); 245 | } 246 | 247 | constexpr friend bool operator<(span_iterator lhs, 248 | span_iterator rhs) noexcept 249 | { 250 | return lhs.index_ < rhs.index_; 251 | } 252 | 253 | constexpr friend bool operator<=(span_iterator lhs, 254 | span_iterator rhs) noexcept 255 | { 256 | return !(rhs < lhs); 257 | } 258 | 259 | constexpr friend bool operator>(span_iterator lhs, 260 | span_iterator rhs) noexcept 261 | { 262 | return rhs < lhs; 263 | } 264 | 265 | constexpr friend bool operator>=(span_iterator lhs, 266 | span_iterator rhs) noexcept 267 | { 268 | return !(rhs > lhs); 269 | } 270 | 271 | #ifdef _MSC_VER 272 | // MSVC++ iterator debugging support; allows STL algorithms in 15.8+ 273 | // to unwrap span_iterator to a pointer type after a range check in STL 274 | // algorithm calls 275 | friend constexpr void _Verify_range(span_iterator lhs, 276 | span_iterator rhs) noexcept 277 | { // test that [lhs, rhs) forms a valid range inside an STL algorithm 278 | Expects(lhs.span_ == rhs.span_ // range spans have to match 279 | && lhs.index_ <= rhs.index_); // range must not be transposed 280 | } 281 | 282 | constexpr void _Verify_offset(const difference_type n) const noexcept 283 | { // test that the iterator *this + n is a valid range in an STL 284 | // algorithm call 285 | Expects((index_ + n) >= 0 && (index_ + n) <= span_->size()); 286 | } 287 | 288 | constexpr pointer _Unwrapped() const noexcept 289 | { // after seeking *this to a high water mark, or using one of the 290 | // _Verify_xxx functions above, unwrap this span_iterator to a raw 291 | // pointer 292 | return span_->data() + index_; 293 | } 294 | 295 | // Tell the STL that span_iterator should not be unwrapped if it can't 296 | // validate in advance, even in release / optimized builds: 297 | #if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) 298 | static constexpr const bool _Unwrap_when_unverified = false; 299 | #else 300 | static constexpr bool _Unwrap_when_unverified = false; 301 | #endif 302 | constexpr void _Seek_to(const pointer p) noexcept 303 | { // adjust the position of *this to previously verified location p 304 | // after _Unwrapped 305 | index_ = p - span_->data(); 306 | } 307 | #endif 308 | 309 | protected: 310 | const Span* span_ = nullptr; 311 | std::ptrdiff_t index_ = 0; 312 | }; 313 | 314 | template 315 | class extent_type 316 | { 317 | public: 318 | using index_type = std::ptrdiff_t; 319 | 320 | static_assert(Ext >= 0, "A fixed-size span must be >= 0 in size."); 321 | 322 | constexpr extent_type() noexcept {} 323 | 324 | template 325 | constexpr extent_type(extent_type ext) 326 | { 327 | static_assert(Other == Ext || Other == dynamic_extent, 328 | "Mismatch between fixed-size extent and size of initializing data."); 329 | Expects(ext.size() == Ext); 330 | } 331 | 332 | constexpr extent_type(index_type size) { Expects(size == Ext); } 333 | 334 | constexpr index_type size() const noexcept { return Ext; } 335 | }; 336 | 337 | template <> 338 | class extent_type 339 | { 340 | public: 341 | using index_type = std::ptrdiff_t; 342 | 343 | template 344 | explicit constexpr extent_type(extent_type ext) : size_(ext.size()) 345 | { 346 | } 347 | 348 | explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } 349 | 350 | constexpr index_type size() const noexcept { return size_; } 351 | 352 | private: 353 | index_type size_; 354 | }; 355 | 356 | template 357 | struct calculate_subspan_type 358 | { 359 | using type = span; 360 | }; 361 | } // namespace details 362 | 363 | // [span], class template span 364 | template 365 | class span 366 | { 367 | public: 368 | // constants and types 369 | using element_type = ElementType; 370 | using value_type = std::remove_cv_t; 371 | using index_type = std::ptrdiff_t; 372 | using pointer = element_type*; 373 | using reference = element_type&; 374 | 375 | using iterator = details::span_iterator, false>; 376 | using const_iterator = details::span_iterator, true>; 377 | using reverse_iterator = std::reverse_iterator; 378 | using const_reverse_iterator = std::reverse_iterator; 379 | 380 | using size_type = index_type; 381 | 382 | #if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) 383 | static constexpr const index_type extent { Extent }; 384 | #else 385 | static constexpr index_type extent { Extent }; 386 | #endif 387 | 388 | // [span.cons], span constructors, copy, assignment, and destructor 389 | template " SFINAE, 391 | // since "std::enable_if_t" is ill-formed when Extent is greater than 0. 392 | class = std::enable_if_t<(Dependent || Extent <= 0)>> 393 | constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) 394 | { 395 | } 396 | 397 | constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {} 398 | 399 | constexpr span(pointer firstElem, pointer lastElem) 400 | : storage_(firstElem, std::distance(firstElem, lastElem)) 401 | { 402 | } 403 | 404 | template 405 | constexpr span(element_type (&arr)[N]) noexcept 406 | : storage_(KnownNotNull{&arr[0]}, details::extent_type()) 407 | { 408 | } 409 | 410 | template > 411 | constexpr span(std::array& arr) noexcept 412 | : storage_(&arr[0], details::extent_type()) 413 | { 414 | } 415 | 416 | template 417 | constexpr span(const std::array, N>& arr) noexcept 418 | : storage_(&arr[0], details::extent_type()) 419 | { 420 | } 421 | 422 | // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement 423 | // on Container to be a contiguous sequence container. 424 | template ::value && !details::is_std_array::value && 427 | std::is_convertible::value && 428 | std::is_convertible().data())>::value>> 430 | constexpr span(Container& cont) : span(cont.data(), narrow(cont.size())) 431 | { 432 | } 433 | 434 | template ::value && !details::is_span::value && 437 | std::is_convertible::value && 438 | std::is_convertible().data())>::value>> 440 | constexpr span(const Container& cont) : span(cont.data(), narrow(cont.size())) 441 | { 442 | } 443 | 444 | constexpr span(const span& other) noexcept = default; 445 | 446 | template < 447 | class OtherElementType, std::ptrdiff_t OtherExtent, 448 | class = std::enable_if_t< 449 | details::is_allowed_extent_conversion::value && 450 | details::is_allowed_element_type_conversion::value>> 451 | constexpr span(const span& other) 452 | : storage_(other.data(), details::extent_type(other.size())) 453 | { 454 | } 455 | 456 | ~span() noexcept = default; 457 | constexpr span& operator=(const span& other) noexcept = default; 458 | 459 | // [span.sub], span subviews 460 | template 461 | constexpr span first() const 462 | { 463 | Expects(Count >= 0 && Count <= size()); 464 | return {data(), Count}; 465 | } 466 | 467 | template 468 | constexpr span last() const 469 | { 470 | Expects(Count >= 0 && size() - Count >= 0); 471 | return {data() + (size() - Count), Count}; 472 | } 473 | 474 | template 475 | constexpr auto subspan() const -> typename details::calculate_subspan_type::type 476 | { 477 | Expects((Offset >= 0 && size() - Offset >= 0) && 478 | (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size()))); 479 | 480 | return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count}; 481 | } 482 | 483 | constexpr span first(index_type count) const 484 | { 485 | Expects(count >= 0 && count <= size()); 486 | return {data(), count}; 487 | } 488 | 489 | constexpr span last(index_type count) const 490 | { 491 | return make_subspan(size() - count, dynamic_extent, subspan_selector{}); 492 | } 493 | 494 | constexpr span subspan(index_type offset, 495 | index_type count = dynamic_extent) const 496 | { 497 | return make_subspan(offset, count, subspan_selector{}); 498 | } 499 | 500 | 501 | // [span.obs], span observers 502 | constexpr index_type size() const noexcept { return storage_.size(); } 503 | constexpr index_type size_bytes() const noexcept 504 | { 505 | return size() * narrow_cast(sizeof(element_type)); 506 | } 507 | constexpr bool empty() const noexcept { return size() == 0; } 508 | 509 | // [span.elem], span element access 510 | constexpr reference operator[](index_type idx) const 511 | { 512 | Expects(idx >= 0 && idx < storage_.size()); 513 | return data()[idx]; 514 | } 515 | 516 | constexpr reference at(index_type idx) const { return this->operator[](idx); } 517 | constexpr reference operator()(index_type idx) const { return this->operator[](idx); } 518 | constexpr pointer data() const noexcept { return storage_.data(); } 519 | 520 | // [span.iter], span iterator support 521 | constexpr iterator begin() const noexcept { return {this, 0}; } 522 | constexpr iterator end() const noexcept { return {this, size()}; } 523 | 524 | constexpr const_iterator cbegin() const noexcept { return {this, 0}; } 525 | constexpr const_iterator cend() const noexcept { return {this, size()}; } 526 | 527 | constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } 528 | constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } 529 | 530 | constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; } 531 | constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; } 532 | 533 | #ifdef _MSC_VER 534 | // Tell MSVC how to unwrap spans in range-based-for 535 | constexpr pointer _Unchecked_begin() const noexcept { return data(); } 536 | constexpr pointer _Unchecked_end() const noexcept { return data() + size(); } 537 | #endif // _MSC_VER 538 | 539 | private: 540 | 541 | // Needed to remove unnecessary null check in subspans 542 | struct KnownNotNull 543 | { 544 | pointer p; 545 | }; 546 | 547 | // this implementation detail class lets us take advantage of the 548 | // empty base class optimization to pay for only storage of a single 549 | // pointer in the case of fixed-size spans 550 | template 551 | class storage_type : public ExtentType 552 | { 553 | public: 554 | // KnownNotNull parameter is needed to remove unnecessary null check 555 | // in subspans and constructors from arrays 556 | template 557 | constexpr storage_type(KnownNotNull data, OtherExtentType ext) : ExtentType(ext), data_(data.p) 558 | { 559 | Expects(ExtentType::size() >= 0); 560 | } 561 | 562 | 563 | template 564 | constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) 565 | { 566 | Expects(ExtentType::size() >= 0); 567 | Expects(data || ExtentType::size() == 0); 568 | } 569 | 570 | constexpr pointer data() const noexcept { return data_; } 571 | 572 | private: 573 | pointer data_; 574 | }; 575 | 576 | storage_type> storage_; 577 | 578 | // The rest is needed to remove unnecessary null check 579 | // in subspans and constructors from arrays 580 | constexpr span(KnownNotNull ptr, index_type count) : storage_(ptr, count) {} 581 | 582 | template 583 | class subspan_selector {}; 584 | 585 | template 586 | span make_subspan(index_type offset, 587 | index_type count, 588 | subspan_selector) const 589 | { 590 | span tmp(*this); 591 | return tmp.subspan(offset, count); 592 | } 593 | 594 | span make_subspan(index_type offset, 595 | index_type count, 596 | subspan_selector) const 597 | { 598 | Expects(offset >= 0 && size() - offset >= 0); 599 | if (count == dynamic_extent) 600 | { 601 | return { KnownNotNull{ data() + offset }, size() - offset }; 602 | } 603 | 604 | Expects(count >= 0 && size() - offset >= count); 605 | return { KnownNotNull{ data() + offset }, count }; 606 | } 607 | }; 608 | 609 | #if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) 610 | template 611 | constexpr const typename span::index_type span::extent; 612 | #endif 613 | 614 | 615 | // [span.comparison], span comparison operators 616 | template 617 | constexpr bool operator==(span l, 618 | span r) 619 | { 620 | return std::equal(l.begin(), l.end(), r.begin(), r.end()); 621 | } 622 | 623 | template 624 | constexpr bool operator!=(span l, 625 | span r) 626 | { 627 | return !(l == r); 628 | } 629 | 630 | template 631 | constexpr bool operator<(span l, 632 | span r) 633 | { 634 | return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); 635 | } 636 | 637 | template 638 | constexpr bool operator<=(span l, 639 | span r) 640 | { 641 | return !(l > r); 642 | } 643 | 644 | template 645 | constexpr bool operator>(span l, 646 | span r) 647 | { 648 | return r < l; 649 | } 650 | 651 | template 652 | constexpr bool operator>=(span l, 653 | span r) 654 | { 655 | return !(l < r); 656 | } 657 | 658 | namespace details 659 | { 660 | // if we only supported compilers with good constexpr support then 661 | // this pair of classes could collapse down to a constexpr function 662 | 663 | // we should use a narrow_cast<> to go to std::size_t, but older compilers may not see it as 664 | // constexpr 665 | // and so will fail compilation of the template 666 | template 667 | struct calculate_byte_size 668 | : std::integral_constant(sizeof(ElementType) * 670 | static_cast(Extent))> 671 | { 672 | }; 673 | 674 | template 675 | struct calculate_byte_size 676 | : std::integral_constant 677 | { 678 | }; 679 | } 680 | 681 | // [span.objectrep], views of object representation 682 | template 683 | span::value> 684 | as_bytes(span s) noexcept 685 | { 686 | return {reinterpret_cast(s.data()), s.size_bytes()}; 687 | } 688 | 689 | template ::value>> 691 | span::value> 692 | as_writeable_bytes(span s) noexcept 693 | { 694 | return {reinterpret_cast(s.data()), s.size_bytes()}; 695 | } 696 | 697 | // 698 | // make_span() - Utility functions for creating spans 699 | // 700 | template 701 | constexpr span make_span(ElementType* ptr, typename span::index_type count) 702 | { 703 | return span(ptr, count); 704 | } 705 | 706 | template 707 | constexpr span make_span(ElementType* firstElem, ElementType* lastElem) 708 | { 709 | return span(firstElem, lastElem); 710 | } 711 | 712 | template 713 | constexpr span make_span(ElementType (&arr)[N]) noexcept 714 | { 715 | return span(arr); 716 | } 717 | 718 | template 719 | constexpr span make_span(Container& cont) 720 | { 721 | return span(cont); 722 | } 723 | 724 | template 725 | constexpr span make_span(const Container& cont) 726 | { 727 | return span(cont); 728 | } 729 | 730 | template 731 | constexpr span make_span(Ptr& cont, std::ptrdiff_t count) 732 | { 733 | return span(cont, count); 734 | } 735 | 736 | template 737 | constexpr span make_span(Ptr& cont) 738 | { 739 | return span(cont); 740 | } 741 | 742 | // Specialization of gsl::at for span 743 | template 744 | constexpr ElementType& at(span s, index i) 745 | { 746 | // No bounds checking here because it is done in span::operator[] called below 747 | return s[i]; 748 | } 749 | 750 | } // namespace gsl 751 | 752 | #ifdef _MSC_VER 753 | #if _MSC_VER < 1910 754 | #undef constexpr 755 | #pragma pop_macro("constexpr") 756 | 757 | #endif // _MSC_VER < 1910 758 | 759 | #pragma warning(pop) 760 | #endif // _MSC_VER 761 | 762 | #if __GNUC__ > 6 763 | #pragma GCC diagnostic pop 764 | #endif // __GNUC__ > 6 765 | 766 | #endif // GSL_SPAN_H 767 | -------------------------------------------------------------------------------- /gsl/string_span: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4 | // 5 | // This code is licensed under the MIT License (MIT). 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13 | // THE SOFTWARE. 14 | // 15 | /////////////////////////////////////////////////////////////////////////////// 16 | 17 | #ifndef GSL_STRING_SPAN_H 18 | #define GSL_STRING_SPAN_H 19 | 20 | #include "gsl_assert" // for Ensures, Expects 21 | #include "gsl_util" // for narrow_cast 22 | #include "span" // for operator!=, operator==, dynamic_extent 23 | 24 | #include // for equal, lexicographical_compare 25 | #include // for array 26 | #include // for ptrdiff_t, size_t, nullptr_t 27 | #include // for PTRDIFF_MAX 28 | #include 29 | #include // for basic_string, allocator, char_traits 30 | #include // for declval, is_convertible, enable_if_t, add_... 31 | 32 | #ifdef _MSC_VER 33 | #pragma warning(push) 34 | 35 | // blanket turn off warnings from CppCoreCheck for now 36 | // so people aren't annoyed by them when running the tool. 37 | // more targeted suppressions will be added in a future update to the GSL 38 | #pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) 39 | 40 | #if _MSC_VER < 1910 41 | #pragma push_macro("constexpr") 42 | #define constexpr /*constexpr*/ 43 | 44 | #endif // _MSC_VER < 1910 45 | #endif // _MSC_VER 46 | 47 | // In order to test the library, we need it to throw exceptions that we can catch 48 | #ifdef GSL_THROW_ON_CONTRACT_VIOLATION 49 | #define GSL_NOEXCEPT /*noexcept*/ 50 | #else 51 | #define GSL_NOEXCEPT noexcept 52 | #endif // GSL_THROW_ON_CONTRACT_VIOLATION 53 | 54 | namespace gsl 55 | { 56 | // 57 | // czstring and wzstring 58 | // 59 | // These are "tag" typedefs for C-style strings (i.e. null-terminated character arrays) 60 | // that allow static analysis to help find bugs. 61 | // 62 | // There are no additional features/semantics that we can find a way to add inside the 63 | // type system for these types that will not either incur significant runtime costs or 64 | // (sometimes needlessly) break existing programs when introduced. 65 | // 66 | 67 | template 68 | using basic_zstring = CharT*; 69 | 70 | template 71 | using czstring = basic_zstring; 72 | 73 | template 74 | using cwzstring = basic_zstring; 75 | 76 | template 77 | using cu16zstring = basic_zstring; 78 | 79 | template 80 | using cu32zstring = basic_zstring; 81 | 82 | template 83 | using zstring = basic_zstring; 84 | 85 | template 86 | using wzstring = basic_zstring; 87 | 88 | template 89 | using u16zstring = basic_zstring; 90 | 91 | template 92 | using u32zstring = basic_zstring; 93 | 94 | namespace details 95 | { 96 | template 97 | std::ptrdiff_t string_length(const CharT* str, std::ptrdiff_t n) 98 | { 99 | if (str == nullptr || n <= 0) return 0; 100 | 101 | const span str_span{str, n}; 102 | 103 | std::ptrdiff_t len = 0; 104 | while (len < n && str_span[len]) len++; 105 | 106 | return len; 107 | } 108 | } 109 | 110 | // 111 | // ensure_sentinel() 112 | // 113 | // Provides a way to obtain an span from a contiguous sequence 114 | // that ends with a (non-inclusive) sentinel value. 115 | // 116 | // Will fail-fast if sentinel cannot be found before max elements are examined. 117 | // 118 | template 119 | span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) 120 | { 121 | auto cur = seq; 122 | while ((cur - seq) < max && *cur != Sentinel) ++cur; 123 | Ensures(*cur == Sentinel); 124 | return {seq, cur - seq}; 125 | } 126 | 127 | // 128 | // ensure_z - creates a span for a zero terminated strings. 129 | // Will fail fast if a null-terminator cannot be found before 130 | // the limit of size_type. 131 | // 132 | template 133 | span ensure_z(CharT* const& sz, std::ptrdiff_t max = PTRDIFF_MAX) 134 | { 135 | return ensure_sentinel(sz, max); 136 | } 137 | 138 | template 139 | span ensure_z(CharT (&sz)[N]) 140 | { 141 | return ensure_z(&sz[0], static_cast(N)); 142 | } 143 | 144 | template 145 | span::type, dynamic_extent> 146 | ensure_z(Cont& cont) 147 | { 148 | return ensure_z(cont.data(), static_cast(cont.size())); 149 | } 150 | 151 | template 152 | class basic_string_span; 153 | 154 | namespace details 155 | { 156 | template 157 | struct is_basic_string_span_oracle : std::false_type 158 | { 159 | }; 160 | 161 | template 162 | struct is_basic_string_span_oracle> : std::true_type 163 | { 164 | }; 165 | 166 | template 167 | struct is_basic_string_span : is_basic_string_span_oracle> 168 | { 169 | }; 170 | } 171 | 172 | // 173 | // string_span and relatives 174 | // 175 | template 176 | class basic_string_span 177 | { 178 | public: 179 | using element_type = CharT; 180 | using pointer = std::add_pointer_t; 181 | using reference = std::add_lvalue_reference_t; 182 | using const_reference = std::add_lvalue_reference_t>; 183 | using impl_type = span; 184 | 185 | using index_type = typename impl_type::index_type; 186 | using iterator = typename impl_type::iterator; 187 | using const_iterator = typename impl_type::const_iterator; 188 | using reverse_iterator = typename impl_type::reverse_iterator; 189 | using const_reverse_iterator = typename impl_type::const_reverse_iterator; 190 | 191 | // default (empty) 192 | constexpr basic_string_span() GSL_NOEXCEPT = default; 193 | 194 | // copy 195 | constexpr basic_string_span(const basic_string_span& other) GSL_NOEXCEPT = default; 196 | 197 | // assign 198 | constexpr basic_string_span& operator=(const basic_string_span& other) GSL_NOEXCEPT = default; 199 | 200 | constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {} 201 | constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {} 202 | 203 | // From static arrays - if 0-terminated, remove 0 from the view 204 | // All other containers allow 0s within the length, so we do not remove them 205 | template 206 | constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr)) 207 | { 208 | } 209 | 210 | template > 211 | constexpr basic_string_span(std::array& arr) GSL_NOEXCEPT : span_(arr) 212 | { 213 | } 214 | 215 | template > 216 | constexpr basic_string_span(const std::array& arr) GSL_NOEXCEPT 217 | : span_(arr) 218 | { 219 | } 220 | 221 | // Container signature should work for basic_string after C++17 version exists 222 | template 223 | constexpr basic_string_span(std::basic_string& str) 224 | : span_(&str[0], narrow_cast(str.length())) 225 | { 226 | } 227 | 228 | template 229 | constexpr basic_string_span(const std::basic_string& str) 230 | : span_(&str[0], str.length()) 231 | { 232 | } 233 | 234 | // from containers. Containers must have a pointer type and data() function signatures 235 | template ::value && 238 | std::is_convertible::value && 239 | std::is_convertible().data())>::value>> 241 | constexpr basic_string_span(Container& cont) : span_(cont) 242 | { 243 | } 244 | 245 | template ::value && 248 | std::is_convertible::value && 249 | std::is_convertible().data())>::value>> 251 | constexpr basic_string_span(const Container& cont) : span_(cont) 252 | { 253 | } 254 | 255 | // from string_span 256 | template < 257 | class OtherValueType, std::ptrdiff_t OtherExtent, 258 | class = std::enable_if_t::impl_type, impl_type>::value>> 260 | constexpr basic_string_span(basic_string_span other) 261 | : span_(other.data(), other.length()) 262 | { 263 | } 264 | 265 | template 266 | constexpr basic_string_span first() const 267 | { 268 | return {span_.template first()}; 269 | } 270 | 271 | constexpr basic_string_span first(index_type count) const 272 | { 273 | return {span_.first(count)}; 274 | } 275 | 276 | template 277 | constexpr basic_string_span last() const 278 | { 279 | return {span_.template last()}; 280 | } 281 | 282 | constexpr basic_string_span last(index_type count) const 283 | { 284 | return {span_.last(count)}; 285 | } 286 | 287 | template 288 | constexpr basic_string_span subspan() const 289 | { 290 | return {span_.template subspan()}; 291 | } 292 | 293 | constexpr basic_string_span 294 | subspan(index_type offset, index_type count = dynamic_extent) const 295 | { 296 | return {span_.subspan(offset, count)}; 297 | } 298 | 299 | constexpr reference operator[](index_type idx) const { return span_[idx]; } 300 | constexpr reference operator()(index_type idx) const { return span_[idx]; } 301 | 302 | constexpr pointer data() const { return span_.data(); } 303 | 304 | constexpr index_type length() const GSL_NOEXCEPT { return span_.size(); } 305 | constexpr index_type size() const GSL_NOEXCEPT { return span_.size(); } 306 | constexpr index_type size_bytes() const GSL_NOEXCEPT { return span_.size_bytes(); } 307 | constexpr index_type length_bytes() const GSL_NOEXCEPT { return span_.length_bytes(); } 308 | constexpr bool empty() const GSL_NOEXCEPT { return size() == 0; } 309 | 310 | constexpr iterator begin() const GSL_NOEXCEPT { return span_.begin(); } 311 | constexpr iterator end() const GSL_NOEXCEPT { return span_.end(); } 312 | 313 | constexpr const_iterator cbegin() const GSL_NOEXCEPT { return span_.cbegin(); } 314 | constexpr const_iterator cend() const GSL_NOEXCEPT { return span_.cend(); } 315 | 316 | constexpr reverse_iterator rbegin() const GSL_NOEXCEPT { return span_.rbegin(); } 317 | constexpr reverse_iterator rend() const GSL_NOEXCEPT { return span_.rend(); } 318 | 319 | constexpr const_reverse_iterator crbegin() const GSL_NOEXCEPT { return span_.crbegin(); } 320 | constexpr const_reverse_iterator crend() const GSL_NOEXCEPT { return span_.crend(); } 321 | 322 | private: 323 | static impl_type remove_z(pointer const& sz, std::ptrdiff_t max) 324 | { 325 | return {sz, details::string_length(sz, max)}; 326 | } 327 | 328 | template 329 | static impl_type remove_z(element_type (&sz)[N]) 330 | { 331 | return remove_z(&sz[0], narrow_cast(N)); 332 | } 333 | 334 | impl_type span_; 335 | }; 336 | 337 | template 338 | using string_span = basic_string_span; 339 | 340 | template 341 | using cstring_span = basic_string_span; 342 | 343 | template 344 | using wstring_span = basic_string_span; 345 | 346 | template 347 | using cwstring_span = basic_string_span; 348 | 349 | template 350 | using u16string_span = basic_string_span; 351 | 352 | template 353 | using cu16string_span = basic_string_span; 354 | 355 | template 356 | using u32string_span = basic_string_span; 357 | 358 | template 359 | using cu32string_span = basic_string_span; 360 | 361 | // 362 | // to_string() allow (explicit) conversions from string_span to string 363 | // 364 | 365 | template 366 | std::basic_string::type> 367 | to_string(basic_string_span view) 368 | { 369 | return {view.data(), static_cast(view.length())}; 370 | } 371 | 372 | template , 373 | typename Allocator = std::allocator, typename gCharT, std::ptrdiff_t Extent> 374 | std::basic_string to_basic_string(basic_string_span view) 375 | { 376 | return {view.data(), static_cast(view.length())}; 377 | } 378 | 379 | template 380 | basic_string_span::value> 381 | as_bytes(basic_string_span s) noexcept 382 | { 383 | return { reinterpret_cast(s.data()), s.size_bytes() }; 384 | } 385 | 386 | template ::value>> 388 | basic_string_span::value> 389 | as_writeable_bytes(basic_string_span s) noexcept 390 | { 391 | return {reinterpret_cast(s.data()), s.size_bytes()}; 392 | } 393 | 394 | // zero-terminated string span, used to convert 395 | // zero-terminated spans to legacy strings 396 | template 397 | class basic_zstring_span 398 | { 399 | public: 400 | using value_type = CharT; 401 | using const_value_type = std::add_const_t; 402 | 403 | using pointer = std::add_pointer_t; 404 | using const_pointer = std::add_pointer_t; 405 | 406 | using zstring_type = basic_zstring; 407 | using const_zstring_type = basic_zstring; 408 | 409 | using impl_type = span; 410 | using string_span_type = basic_string_span; 411 | 412 | constexpr basic_zstring_span(impl_type s) GSL_NOEXCEPT : span_(s) 413 | { 414 | // expects a zero-terminated span 415 | Expects(s[s.size() - 1] == '\0'); 416 | } 417 | 418 | // copy 419 | constexpr basic_zstring_span(const basic_zstring_span& other) = default; 420 | 421 | // move 422 | constexpr basic_zstring_span(basic_zstring_span&& other) = default; 423 | 424 | // assign 425 | constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default; 426 | 427 | // move assign 428 | constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default; 429 | 430 | constexpr bool empty() const GSL_NOEXCEPT { return span_.size() == 0; } 431 | 432 | constexpr string_span_type as_string_span() const GSL_NOEXCEPT 433 | { 434 | auto sz = span_.size(); 435 | return { span_.data(), sz > 1 ? sz - 1 : 0 }; 436 | } 437 | constexpr string_span_type ensure_z() const GSL_NOEXCEPT { return gsl::ensure_z(span_); } 438 | 439 | constexpr const_zstring_type assume_z() const GSL_NOEXCEPT { return span_.data(); } 440 | 441 | private: 442 | impl_type span_; 443 | }; 444 | 445 | template 446 | using zstring_span = basic_zstring_span; 447 | 448 | template 449 | using wzstring_span = basic_zstring_span; 450 | 451 | template 452 | using u16zstring_span = basic_zstring_span; 453 | 454 | template 455 | using u32zstring_span = basic_zstring_span; 456 | 457 | template 458 | using czstring_span = basic_zstring_span; 459 | 460 | template 461 | using cwzstring_span = basic_zstring_span; 462 | 463 | template 464 | using cu16zstring_span = basic_zstring_span; 465 | 466 | template 467 | using cu32zstring_span = basic_zstring_span; 468 | 469 | // operator == 470 | template ::value || 473 | std::is_convertible>>::value>> 474 | bool operator==(const gsl::basic_string_span& one, const T& other) GSL_NOEXCEPT 475 | { 476 | const gsl::basic_string_span> tmp(other); 477 | return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); 478 | } 479 | 480 | template ::value && 483 | std::is_convertible>>::value>> 484 | bool operator==(const T& one, const gsl::basic_string_span& other) GSL_NOEXCEPT 485 | { 486 | gsl::basic_string_span> tmp(one); 487 | return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); 488 | } 489 | 490 | // operator != 491 | template , Extent>>::value>> 494 | bool operator!=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT 495 | { 496 | return !(one == other); 497 | } 498 | 499 | template < 500 | typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, 501 | typename = std::enable_if_t< 502 | std::is_convertible, Extent>>::value && 503 | !gsl::details::is_basic_string_span::value>> 504 | bool operator!=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT 505 | { 506 | return !(one == other); 507 | } 508 | 509 | // operator< 510 | template , Extent>>::value>> 513 | bool operator<(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT 514 | { 515 | const gsl::basic_string_span, Extent> tmp(other); 516 | return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); 517 | } 518 | 519 | template < 520 | typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, 521 | typename = std::enable_if_t< 522 | std::is_convertible, Extent>>::value && 523 | !gsl::details::is_basic_string_span::value>> 524 | bool operator<(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT 525 | { 526 | gsl::basic_string_span, Extent> tmp(one); 527 | return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); 528 | } 529 | 530 | #ifndef _MSC_VER 531 | 532 | // VS treats temp and const containers as convertible to basic_string_span, 533 | // so the cases below are already covered by the previous operators 534 | 535 | template < 536 | typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, 537 | typename DataType = typename T::value_type, 538 | typename = std::enable_if_t< 539 | !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && 540 | std::is_convertible::value && 541 | std::is_same().size(), *std::declval().data())>, 542 | DataType>::value>> 543 | bool operator<(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT 544 | { 545 | gsl::basic_string_span, Extent> tmp(other); 546 | return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); 547 | } 548 | 549 | template < 550 | typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, 551 | typename DataType = typename T::value_type, 552 | typename = std::enable_if_t< 553 | !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && 554 | std::is_convertible::value && 555 | std::is_same().size(), *std::declval().data())>, 556 | DataType>::value>> 557 | bool operator<(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT 558 | { 559 | gsl::basic_string_span, Extent> tmp(one); 560 | return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); 561 | } 562 | #endif 563 | 564 | // operator <= 565 | template , Extent>>::value>> 568 | bool operator<=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT 569 | { 570 | return !(other < one); 571 | } 572 | 573 | template < 574 | typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, 575 | typename = std::enable_if_t< 576 | std::is_convertible, Extent>>::value && 577 | !gsl::details::is_basic_string_span::value>> 578 | bool operator<=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT 579 | { 580 | return !(other < one); 581 | } 582 | 583 | #ifndef _MSC_VER 584 | 585 | // VS treats temp and const containers as convertible to basic_string_span, 586 | // so the cases below are already covered by the previous operators 587 | 588 | template < 589 | typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, 590 | typename DataType = typename T::value_type, 591 | typename = std::enable_if_t< 592 | !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && 593 | std::is_convertible::value && 594 | std::is_same().size(), *std::declval().data())>, 595 | DataType>::value>> 596 | bool operator<=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT 597 | { 598 | return !(other < one); 599 | } 600 | 601 | template < 602 | typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, 603 | typename DataType = typename T::value_type, 604 | typename = std::enable_if_t< 605 | !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && 606 | std::is_convertible::value && 607 | std::is_same().size(), *std::declval().data())>, 608 | DataType>::value>> 609 | bool operator<=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT 610 | { 611 | return !(other < one); 612 | } 613 | #endif 614 | 615 | // operator> 616 | template , Extent>>::value>> 619 | bool operator>(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT 620 | { 621 | return other < one; 622 | } 623 | 624 | template < 625 | typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, 626 | typename = std::enable_if_t< 627 | std::is_convertible, Extent>>::value && 628 | !gsl::details::is_basic_string_span::value>> 629 | bool operator>(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT 630 | { 631 | return other < one; 632 | } 633 | 634 | #ifndef _MSC_VER 635 | 636 | // VS treats temp and const containers as convertible to basic_string_span, 637 | // so the cases below are already covered by the previous operators 638 | 639 | template < 640 | typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, 641 | typename DataType = typename T::value_type, 642 | typename = std::enable_if_t< 643 | !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && 644 | std::is_convertible::value && 645 | std::is_same().size(), *std::declval().data())>, 646 | DataType>::value>> 647 | bool operator>(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT 648 | { 649 | return other < one; 650 | } 651 | 652 | template < 653 | typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, 654 | typename DataType = typename T::value_type, 655 | typename = std::enable_if_t< 656 | !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && 657 | std::is_convertible::value && 658 | std::is_same().size(), *std::declval().data())>, 659 | DataType>::value>> 660 | bool operator>(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT 661 | { 662 | return other < one; 663 | } 664 | #endif 665 | 666 | // operator >= 667 | template , Extent>>::value>> 670 | bool operator>=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT 671 | { 672 | return !(one < other); 673 | } 674 | 675 | template < 676 | typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, 677 | typename = std::enable_if_t< 678 | std::is_convertible, Extent>>::value && 679 | !gsl::details::is_basic_string_span::value>> 680 | bool operator>=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT 681 | { 682 | return !(one < other); 683 | } 684 | 685 | #ifndef _MSC_VER 686 | 687 | // VS treats temp and const containers as convertible to basic_string_span, 688 | // so the cases below are already covered by the previous operators 689 | 690 | template < 691 | typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, 692 | typename DataType = typename T::value_type, 693 | typename = std::enable_if_t< 694 | !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && 695 | std::is_convertible::value && 696 | std::is_same().size(), *std::declval().data())>, 697 | DataType>::value>> 698 | bool operator>=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT 699 | { 700 | return !(one < other); 701 | } 702 | 703 | template < 704 | typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, 705 | typename DataType = typename T::value_type, 706 | typename = std::enable_if_t< 707 | !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && 708 | std::is_convertible::value && 709 | std::is_same().size(), *std::declval().data())>, 710 | DataType>::value>> 711 | bool operator>=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT 712 | { 713 | return !(one < other); 714 | } 715 | #endif 716 | } // namespace gsl 717 | 718 | #undef GSL_NOEXCEPT 719 | 720 | #ifdef _MSC_VER 721 | #pragma warning(pop) 722 | 723 | #if _MSC_VER < 1910 724 | #undef constexpr 725 | #pragma pop_macro("constexpr") 726 | 727 | #endif // _MSC_VER < 1910 728 | #endif // _MSC_VER 729 | 730 | #endif // GSL_STRING_SPAN_H 731 | -------------------------------------------------------------------------------- /how_to_build.md: -------------------------------------------------------------------------------- 1 | # How to build 2 | 3 | You can create projects to build the tests for the uuid library using CMake. 4 | 5 | If you don't have CMake installed you can get it from https://cmake.org/. 6 | 7 | ## Windows 8 | 9 | Do the Following: 10 | 11 | * Create a folder called **build** 12 | ``` 13 | mkdir build 14 | cd build 15 | ``` 16 | 17 | * Run the following CMake command from the **build** folder to generate projects to target the **x86** platform. 18 | ``` 19 | cmake -G "Visual Studio 15 2017" .. 20 | ``` 21 | 22 | To generate projects to target **x64** use the generator **"Visual Studio 15 2017 Win64"**. 23 | 24 | To generate projects to target **ARM** use the generator **"Visual Studio 15 2017 ARM"**. 25 | 26 | 27 | ## Mac 28 | 29 | Do the Following: 30 | 31 | * Create a folder called **build** 32 | ``` 33 | mkdir build 34 | cd build 35 | ``` 36 | 37 | * Run the following CMake command from the **build** folder to generate projects to target the **x86** platform. 38 | ``` 39 | cmake -G Xcode .. 40 | ``` 41 | -------------------------------------------------------------------------------- /include/uuid.h: -------------------------------------------------------------------------------- 1 | #ifndef STDUUID_H 2 | #define STDUUID_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #ifdef __cplusplus 21 | 22 | # if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) 23 | # define LIBUUID_CPP20_OR_GREATER 24 | # endif 25 | 26 | #endif 27 | 28 | 29 | #ifdef LIBUUID_CPP20_OR_GREATER 30 | #include 31 | #else 32 | #include 33 | #endif 34 | 35 | #ifdef _WIN32 36 | 37 | #ifndef WIN32_LEAN_AND_MEAN 38 | #define WIN32_LEAN_AND_MEAN 39 | #endif 40 | #ifndef NOMINMAX 41 | #define NOMINMAX 42 | #endif 43 | 44 | #ifdef UUID_SYSTEM_GENERATOR 45 | #include 46 | #endif 47 | 48 | #ifdef UUID_TIME_GENERATOR 49 | #include 50 | #pragma comment(lib, "IPHLPAPI.lib") 51 | #endif 52 | 53 | #elif defined(__linux__) || defined(__unix__) 54 | 55 | #ifdef UUID_SYSTEM_GENERATOR 56 | #include 57 | #endif 58 | 59 | #elif defined(__APPLE__) 60 | 61 | #ifdef UUID_SYSTEM_GENERATOR 62 | #include 63 | #endif 64 | 65 | #endif 66 | 67 | namespace uuids 68 | { 69 | #ifdef __cpp_lib_span 70 | template 71 | using span = std::span; 72 | #else 73 | template 74 | using span = gsl::span; 75 | #endif 76 | 77 | namespace detail 78 | { 79 | template 80 | [[nodiscard]] constexpr inline unsigned char hex2char(TChar const ch) noexcept 81 | { 82 | if (ch >= static_cast('0') && ch <= static_cast('9')) 83 | return static_cast(ch - static_cast('0')); 84 | if (ch >= static_cast('a') && ch <= static_cast('f')) 85 | return static_cast(10 + ch - static_cast('a')); 86 | if (ch >= static_cast('A') && ch <= static_cast('F')) 87 | return static_cast(10 + ch - static_cast('A')); 88 | return 0; 89 | } 90 | 91 | template 92 | [[nodiscard]] constexpr inline bool is_hex(TChar const ch) noexcept 93 | { 94 | return 95 | (ch >= static_cast('0') && ch <= static_cast('9')) || 96 | (ch >= static_cast('a') && ch <= static_cast('f')) || 97 | (ch >= static_cast('A') && ch <= static_cast('F')); 98 | } 99 | 100 | template 101 | [[nodiscard]] constexpr std::basic_string_view to_string_view(TChar const * str) noexcept 102 | { 103 | if (str) return str; 104 | return {}; 105 | } 106 | 107 | template 108 | [[nodiscard]] 109 | constexpr std::basic_string_view< 110 | typename StringType::value_type, 111 | typename StringType::traits_type> 112 | to_string_view(StringType const & str) noexcept 113 | { 114 | return str; 115 | } 116 | 117 | class sha1 118 | { 119 | public: 120 | using digest32_t = uint32_t[5]; 121 | using digest8_t = uint8_t[20]; 122 | 123 | static constexpr unsigned int block_bytes = 64; 124 | 125 | [[nodiscard]] inline static uint32_t left_rotate(uint32_t value, size_t const count) noexcept 126 | { 127 | return (value << count) ^ (value >> (32 - count)); 128 | } 129 | 130 | sha1() { reset(); } 131 | 132 | void reset() noexcept 133 | { 134 | m_digest[0] = 0x67452301; 135 | m_digest[1] = 0xEFCDAB89; 136 | m_digest[2] = 0x98BADCFE; 137 | m_digest[3] = 0x10325476; 138 | m_digest[4] = 0xC3D2E1F0; 139 | m_blockByteIndex = 0; 140 | m_byteCount = 0; 141 | } 142 | 143 | void process_byte(uint8_t octet) 144 | { 145 | this->m_block[this->m_blockByteIndex++] = octet; 146 | ++this->m_byteCount; 147 | if (m_blockByteIndex == block_bytes) 148 | { 149 | this->m_blockByteIndex = 0; 150 | process_block(); 151 | } 152 | } 153 | 154 | void process_block(void const * const start, void const * const end) 155 | { 156 | const uint8_t* begin = static_cast(start); 157 | const uint8_t* finish = static_cast(end); 158 | while (begin != finish) 159 | { 160 | process_byte(*begin); 161 | begin++; 162 | } 163 | } 164 | 165 | void process_bytes(void const * const data, size_t const len) 166 | { 167 | const uint8_t* block = static_cast(data); 168 | process_block(block, block + len); 169 | } 170 | 171 | uint32_t const * get_digest(digest32_t digest) 172 | { 173 | size_t const bitCount = this->m_byteCount * 8; 174 | process_byte(0x80); 175 | if (this->m_blockByteIndex > 56) { 176 | while (m_blockByteIndex != 0) { 177 | process_byte(0); 178 | } 179 | while (m_blockByteIndex < 56) { 180 | process_byte(0); 181 | } 182 | } 183 | else { 184 | while (m_blockByteIndex < 56) { 185 | process_byte(0); 186 | } 187 | } 188 | process_byte(0); 189 | process_byte(0); 190 | process_byte(0); 191 | process_byte(0); 192 | process_byte(static_cast((bitCount >> 24) & 0xFF)); 193 | process_byte(static_cast((bitCount >> 16) & 0xFF)); 194 | process_byte(static_cast((bitCount >> 8) & 0xFF)); 195 | process_byte(static_cast((bitCount) & 0xFF)); 196 | 197 | memcpy(digest, m_digest, 5 * sizeof(uint32_t)); 198 | return digest; 199 | } 200 | 201 | uint8_t const * get_digest_bytes(digest8_t digest) 202 | { 203 | digest32_t d32; 204 | get_digest(d32); 205 | size_t di = 0; 206 | digest[di++] = static_cast(d32[0] >> 24); 207 | digest[di++] = static_cast(d32[0] >> 16); 208 | digest[di++] = static_cast(d32[0] >> 8); 209 | digest[di++] = static_cast(d32[0] >> 0); 210 | 211 | digest[di++] = static_cast(d32[1] >> 24); 212 | digest[di++] = static_cast(d32[1] >> 16); 213 | digest[di++] = static_cast(d32[1] >> 8); 214 | digest[di++] = static_cast(d32[1] >> 0); 215 | 216 | digest[di++] = static_cast(d32[2] >> 24); 217 | digest[di++] = static_cast(d32[2] >> 16); 218 | digest[di++] = static_cast(d32[2] >> 8); 219 | digest[di++] = static_cast(d32[2] >> 0); 220 | 221 | digest[di++] = static_cast(d32[3] >> 24); 222 | digest[di++] = static_cast(d32[3] >> 16); 223 | digest[di++] = static_cast(d32[3] >> 8); 224 | digest[di++] = static_cast(d32[3] >> 0); 225 | 226 | digest[di++] = static_cast(d32[4] >> 24); 227 | digest[di++] = static_cast(d32[4] >> 16); 228 | digest[di++] = static_cast(d32[4] >> 8); 229 | digest[di++] = static_cast(d32[4] >> 0); 230 | 231 | return digest; 232 | } 233 | 234 | private: 235 | void process_block() 236 | { 237 | uint32_t w[80]; 238 | for (size_t i = 0; i < 16; i++) { 239 | w[i] = static_cast(m_block[i * 4 + 0] << 24); 240 | w[i] |= static_cast(m_block[i * 4 + 1] << 16); 241 | w[i] |= static_cast(m_block[i * 4 + 2] << 8); 242 | w[i] |= static_cast(m_block[i * 4 + 3]); 243 | } 244 | for (size_t i = 16; i < 80; i++) { 245 | w[i] = left_rotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); 246 | } 247 | 248 | uint32_t a = m_digest[0]; 249 | uint32_t b = m_digest[1]; 250 | uint32_t c = m_digest[2]; 251 | uint32_t d = m_digest[3]; 252 | uint32_t e = m_digest[4]; 253 | 254 | for (std::size_t i = 0; i < 80; ++i) 255 | { 256 | uint32_t f = 0; 257 | uint32_t k = 0; 258 | 259 | if (i < 20) { 260 | f = (b & c) | (~b & d); 261 | k = 0x5A827999; 262 | } 263 | else if (i < 40) { 264 | f = b ^ c ^ d; 265 | k = 0x6ED9EBA1; 266 | } 267 | else if (i < 60) { 268 | f = (b & c) | (b & d) | (c & d); 269 | k = 0x8F1BBCDC; 270 | } 271 | else { 272 | f = b ^ c ^ d; 273 | k = 0xCA62C1D6; 274 | } 275 | uint32_t temp = left_rotate(a, 5) + f + e + k + w[i]; 276 | e = d; 277 | d = c; 278 | c = left_rotate(b, 30); 279 | b = a; 280 | a = temp; 281 | } 282 | 283 | m_digest[0] += a; 284 | m_digest[1] += b; 285 | m_digest[2] += c; 286 | m_digest[3] += d; 287 | m_digest[4] += e; 288 | } 289 | 290 | private: 291 | digest32_t m_digest; 292 | uint8_t m_block[64]; 293 | size_t m_blockByteIndex; 294 | size_t m_byteCount; 295 | }; 296 | 297 | template 298 | inline constexpr CharT empty_guid[37] = "00000000-0000-0000-0000-000000000000"; 299 | 300 | template <> 301 | inline constexpr wchar_t empty_guid[37] = L"00000000-0000-0000-0000-000000000000"; 302 | 303 | template 304 | inline constexpr CharT guid_encoder[17] = "0123456789abcdef"; 305 | 306 | template <> 307 | inline constexpr wchar_t guid_encoder[17] = L"0123456789abcdef"; 308 | } 309 | 310 | // -------------------------------------------------------------------------------------------------------------------------- 311 | // UUID format https://tools.ietf.org/html/rfc4122 312 | // -------------------------------------------------------------------------------------------------------------------------- 313 | 314 | // -------------------------------------------------------------------------------------------------------------------------- 315 | // Field NDR Data Type Octet # Note 316 | // -------------------------------------------------------------------------------------------------------------------------- 317 | // time_low unsigned long 0 - 3 The low field of the timestamp. 318 | // time_mid unsigned short 4 - 5 The middle field of the timestamp. 319 | // time_hi_and_version unsigned short 6 - 7 The high field of the timestamp multiplexed with the version number. 320 | // clock_seq_hi_and_reserved unsigned small 8 The high field of the clock sequence multiplexed with the variant. 321 | // clock_seq_low unsigned small 9 The low field of the clock sequence. 322 | // node character 10 - 15 The spatially unique node identifier. 323 | // -------------------------------------------------------------------------------------------------------------------------- 324 | // 0 1 2 3 325 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 326 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 327 | // | time_low | 328 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 329 | // | time_mid | time_hi_and_version | 330 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 331 | // |clk_seq_hi_res | clk_seq_low | node (0-1) | 332 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 333 | // | node (2-5) | 334 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 335 | 336 | // -------------------------------------------------------------------------------------------------------------------------- 337 | // enumerations 338 | // -------------------------------------------------------------------------------------------------------------------------- 339 | 340 | // indicated by a bit pattern in octet 8, marked with N in xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx 341 | enum class uuid_variant 342 | { 343 | // NCS backward compatibility (with the obsolete Apollo Network Computing System 1.5 UUID format) 344 | // N bit pattern: 0xxx 345 | // > the first 6 octets of the UUID are a 48-bit timestamp (the number of 4 microsecond units of time since 1 Jan 1980 UTC); 346 | // > the next 2 octets are reserved; 347 | // > the next octet is the "address family"; 348 | // > the final 7 octets are a 56-bit host ID in the form specified by the address family 349 | ncs, 350 | 351 | // RFC 4122/DCE 1.1 352 | // N bit pattern: 10xx 353 | // > big-endian byte order 354 | rfc, 355 | 356 | // Microsoft Corporation backward compatibility 357 | // N bit pattern: 110x 358 | // > little endian byte order 359 | // > formely used in the Component Object Model (COM) library 360 | microsoft, 361 | 362 | // reserved for possible future definition 363 | // N bit pattern: 111x 364 | reserved 365 | }; 366 | 367 | // indicated by a bit pattern in octet 6, marked with M in xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx 368 | enum class uuid_version 369 | { 370 | none = 0, // only possible for nil or invalid uuids 371 | time_based = 1, // The time-based version specified in RFC 4122 372 | dce_security = 2, // DCE Security version, with embedded POSIX UIDs. 373 | name_based_md5 = 3, // The name-based version specified in RFS 4122 with MD5 hashing 374 | random_number_based = 4, // The randomly or pseudo-randomly generated version specified in RFS 4122 375 | name_based_sha1 = 5 // The name-based version specified in RFS 4122 with SHA1 hashing 376 | }; 377 | 378 | // Forward declare uuid & to_string so that we can declare to_string as a friend later. 379 | class uuid; 380 | template , 382 | class Allocator = std::allocator> 383 | std::basic_string to_string(uuid const &id); 384 | 385 | // -------------------------------------------------------------------------------------------------------------------------- 386 | // uuid class 387 | // -------------------------------------------------------------------------------------------------------------------------- 388 | class uuid 389 | { 390 | public: 391 | using value_type = uint8_t; 392 | 393 | constexpr uuid() noexcept = default; 394 | 395 | uuid(value_type(&arr)[16]) noexcept 396 | { 397 | std::copy(std::cbegin(arr), std::cend(arr), std::begin(data)); 398 | } 399 | 400 | constexpr uuid(std::array const & arr) noexcept : data{arr} {} 401 | 402 | explicit uuid(span bytes) 403 | { 404 | std::copy(std::cbegin(bytes), std::cend(bytes), std::begin(data)); 405 | } 406 | 407 | template 408 | explicit uuid(ForwardIterator first, ForwardIterator last) 409 | { 410 | if (std::distance(first, last) == 16) 411 | std::copy(first, last, std::begin(data)); 412 | } 413 | 414 | [[nodiscard]] constexpr uuid_variant variant() const noexcept 415 | { 416 | if ((data[8] & 0x80) == 0x00) 417 | return uuid_variant::ncs; 418 | else if ((data[8] & 0xC0) == 0x80) 419 | return uuid_variant::rfc; 420 | else if ((data[8] & 0xE0) == 0xC0) 421 | return uuid_variant::microsoft; 422 | else 423 | return uuid_variant::reserved; 424 | } 425 | 426 | [[nodiscard]] constexpr uuid_version version() const noexcept 427 | { 428 | if ((data[6] & 0xF0) == 0x10) 429 | return uuid_version::time_based; 430 | else if ((data[6] & 0xF0) == 0x20) 431 | return uuid_version::dce_security; 432 | else if ((data[6] & 0xF0) == 0x30) 433 | return uuid_version::name_based_md5; 434 | else if ((data[6] & 0xF0) == 0x40) 435 | return uuid_version::random_number_based; 436 | else if ((data[6] & 0xF0) == 0x50) 437 | return uuid_version::name_based_sha1; 438 | else 439 | return uuid_version::none; 440 | } 441 | 442 | [[nodiscard]] constexpr bool is_nil() const noexcept 443 | { 444 | for (size_t i = 0; i < data.size(); ++i) if (data[i] != 0) return false; 445 | return true; 446 | } 447 | 448 | void swap(uuid & other) noexcept 449 | { 450 | data.swap(other.data); 451 | } 452 | 453 | [[nodiscard]] inline span as_bytes() const 454 | { 455 | return span(reinterpret_cast(data.data()), 16); 456 | } 457 | 458 | template 459 | [[nodiscard]] constexpr static bool is_valid_uuid(StringType const & in_str) noexcept 460 | { 461 | auto str = detail::to_string_view(in_str); 462 | bool firstDigit = true; 463 | size_t hasBraces = 0; 464 | size_t index = 0; 465 | 466 | if (str.empty()) 467 | return false; 468 | 469 | if (str.front() == '{') 470 | hasBraces = 1; 471 | if (hasBraces && str.back() != '}') 472 | return false; 473 | 474 | for (size_t i = hasBraces; i < str.size() - hasBraces; ++i) 475 | { 476 | if (str[i] == '-') continue; 477 | 478 | if (index >= 16 || !detail::is_hex(str[i])) 479 | { 480 | return false; 481 | } 482 | 483 | if (firstDigit) 484 | { 485 | firstDigit = false; 486 | } 487 | else 488 | { 489 | index++; 490 | firstDigit = true; 491 | } 492 | } 493 | 494 | if (index < 16) 495 | { 496 | return false; 497 | } 498 | 499 | return true; 500 | } 501 | 502 | template 503 | [[nodiscard]] constexpr static std::optional from_string(StringType const & in_str) noexcept 504 | { 505 | auto str = detail::to_string_view(in_str); 506 | bool firstDigit = true; 507 | size_t hasBraces = 0; 508 | size_t index = 0; 509 | 510 | std::array data{ { 0 } }; 511 | 512 | if (str.empty()) return {}; 513 | 514 | if (str.front() == '{') 515 | hasBraces = 1; 516 | if (hasBraces && str.back() != '}') 517 | return {}; 518 | 519 | for (size_t i = hasBraces; i < str.size() - hasBraces; ++i) 520 | { 521 | if (str[i] == '-') continue; 522 | 523 | if (index >= 16 || !detail::is_hex(str[i])) 524 | { 525 | return {}; 526 | } 527 | 528 | if (firstDigit) 529 | { 530 | data[index] = static_cast(detail::hex2char(str[i]) << 4); 531 | firstDigit = false; 532 | } 533 | else 534 | { 535 | data[index] = static_cast(data[index] | detail::hex2char(str[i])); 536 | index++; 537 | firstDigit = true; 538 | } 539 | } 540 | 541 | if (index < 16) 542 | { 543 | return {}; 544 | } 545 | 546 | return uuid{ data }; 547 | } 548 | 549 | private: 550 | std::array data{ { 0 } }; 551 | 552 | friend bool operator==(uuid const & lhs, uuid const & rhs) noexcept; 553 | friend bool operator<(uuid const & lhs, uuid const & rhs) noexcept; 554 | 555 | template 556 | friend std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id); 557 | 558 | template 559 | friend std::basic_string to_string(uuid const& id); 560 | 561 | friend std::hash; 562 | }; 563 | 564 | // -------------------------------------------------------------------------------------------------------------------------- 565 | // operators and non-member functions 566 | // -------------------------------------------------------------------------------------------------------------------------- 567 | 568 | [[nodiscard]] inline bool operator== (uuid const& lhs, uuid const& rhs) noexcept 569 | { 570 | return lhs.data == rhs.data; 571 | } 572 | 573 | [[nodiscard]] inline bool operator!= (uuid const& lhs, uuid const& rhs) noexcept 574 | { 575 | return !(lhs == rhs); 576 | } 577 | 578 | [[nodiscard]] inline bool operator< (uuid const& lhs, uuid const& rhs) noexcept 579 | { 580 | return lhs.data < rhs.data; 581 | } 582 | 583 | template 586 | [[nodiscard]] inline std::basic_string to_string(uuid const & id) 587 | { 588 | std::basic_string uustr{detail::empty_guid}; 589 | 590 | for (size_t i = 0, index = 0; i < 36; ++i) 591 | { 592 | if (i == 8 || i == 13 || i == 18 || i == 23) 593 | { 594 | continue; 595 | } 596 | uustr[i] = detail::guid_encoder[id.data[index] >> 4 & 0x0f]; 597 | uustr[++i] = detail::guid_encoder[id.data[index] & 0x0f]; 598 | index++; 599 | } 600 | 601 | return uustr; 602 | } 603 | 604 | template 605 | std::basic_ostream& operator<<(std::basic_ostream& s, uuid const& id) 606 | { 607 | s << to_string(id); 608 | return s; 609 | } 610 | 611 | inline void swap(uuids::uuid & lhs, uuids::uuid & rhs) noexcept 612 | { 613 | lhs.swap(rhs); 614 | } 615 | 616 | // -------------------------------------------------------------------------------------------------------------------------- 617 | // namespace IDs that could be used for generating name-based uuids 618 | // -------------------------------------------------------------------------------------------------------------------------- 619 | 620 | // Name string is a fully-qualified domain name 621 | static uuid uuid_namespace_dns{ {0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} }; 622 | 623 | // Name string is a URL 624 | static uuid uuid_namespace_url{ {0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} }; 625 | 626 | // Name string is an ISO OID (See https://oidref.com/, https://en.wikipedia.org/wiki/Object_identifier) 627 | static uuid uuid_namespace_oid{ {0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} }; 628 | 629 | // Name string is an X.500 DN, in DER or a text output format (See https://en.wikipedia.org/wiki/X.500, https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One) 630 | static uuid uuid_namespace_x500{ {0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} }; 631 | 632 | // -------------------------------------------------------------------------------------------------------------------------- 633 | // uuid generators 634 | // -------------------------------------------------------------------------------------------------------------------------- 635 | 636 | #ifdef UUID_SYSTEM_GENERATOR 637 | class uuid_system_generator 638 | { 639 | public: 640 | using result_type = uuid; 641 | 642 | uuid operator()() 643 | { 644 | #ifdef _WIN32 645 | 646 | GUID newId; 647 | HRESULT hr = ::CoCreateGuid(&newId); 648 | 649 | if (FAILED(hr)) 650 | { 651 | throw std::system_error(hr, std::system_category(), "CoCreateGuid failed"); 652 | } 653 | 654 | std::array bytes = 655 | { { 656 | static_cast((newId.Data1 >> 24) & 0xFF), 657 | static_cast((newId.Data1 >> 16) & 0xFF), 658 | static_cast((newId.Data1 >> 8) & 0xFF), 659 | static_cast((newId.Data1) & 0xFF), 660 | 661 | (unsigned char)((newId.Data2 >> 8) & 0xFF), 662 | (unsigned char)((newId.Data2) & 0xFF), 663 | 664 | (unsigned char)((newId.Data3 >> 8) & 0xFF), 665 | (unsigned char)((newId.Data3) & 0xFF), 666 | 667 | newId.Data4[0], 668 | newId.Data4[1], 669 | newId.Data4[2], 670 | newId.Data4[3], 671 | newId.Data4[4], 672 | newId.Data4[5], 673 | newId.Data4[6], 674 | newId.Data4[7] 675 | } }; 676 | 677 | return uuid{ std::begin(bytes), std::end(bytes) }; 678 | 679 | #elif defined(__linux__) || defined(__unix__) 680 | 681 | uuid_t id; 682 | uuid_generate(id); 683 | 684 | std::array bytes = 685 | { { 686 | id[0], 687 | id[1], 688 | id[2], 689 | id[3], 690 | id[4], 691 | id[5], 692 | id[6], 693 | id[7], 694 | id[8], 695 | id[9], 696 | id[10], 697 | id[11], 698 | id[12], 699 | id[13], 700 | id[14], 701 | id[15] 702 | } }; 703 | 704 | return uuid{ std::begin(bytes), std::end(bytes) }; 705 | 706 | #elif defined(__APPLE__) 707 | auto newId = CFUUIDCreate(NULL); 708 | auto bytes = CFUUIDGetUUIDBytes(newId); 709 | CFRelease(newId); 710 | 711 | std::array arrbytes = 712 | { { 713 | bytes.byte0, 714 | bytes.byte1, 715 | bytes.byte2, 716 | bytes.byte3, 717 | bytes.byte4, 718 | bytes.byte5, 719 | bytes.byte6, 720 | bytes.byte7, 721 | bytes.byte8, 722 | bytes.byte9, 723 | bytes.byte10, 724 | bytes.byte11, 725 | bytes.byte12, 726 | bytes.byte13, 727 | bytes.byte14, 728 | bytes.byte15 729 | } }; 730 | return uuid{ std::begin(arrbytes), std::end(arrbytes) }; 731 | #else 732 | return uuid{}; 733 | #endif 734 | } 735 | }; 736 | #endif 737 | 738 | template 739 | class basic_uuid_random_generator 740 | { 741 | public: 742 | using engine_type = UniformRandomNumberGenerator; 743 | 744 | explicit basic_uuid_random_generator(engine_type& gen) : 745 | generator(&gen, [](auto) {}) {} 746 | explicit basic_uuid_random_generator(engine_type* gen) : 747 | generator(gen, [](auto) {}) {} 748 | 749 | [[nodiscard]] uuid operator()() 750 | { 751 | alignas(uint32_t) uint8_t bytes[16]; 752 | for (int i = 0; i < 16; i += 4) 753 | *reinterpret_cast(bytes + i) = distribution(*generator); 754 | 755 | // variant must be 10xxxxxx 756 | bytes[8] &= 0xBF; 757 | bytes[8] |= 0x80; 758 | 759 | // version must be 0100xxxx 760 | bytes[6] &= 0x4F; 761 | bytes[6] |= 0x40; 762 | 763 | return uuid{std::begin(bytes), std::end(bytes)}; 764 | } 765 | 766 | private: 767 | std::uniform_int_distribution distribution; 768 | std::shared_ptr generator; 769 | }; 770 | 771 | using uuid_random_generator = basic_uuid_random_generator; 772 | 773 | class uuid_name_generator 774 | { 775 | public: 776 | explicit uuid_name_generator(uuid const& namespace_uuid) noexcept 777 | : nsuuid(namespace_uuid) 778 | {} 779 | 780 | template 781 | [[nodiscard]] uuid operator()(StringType const & name) 782 | { 783 | reset(); 784 | process_characters(detail::to_string_view(name)); 785 | return make_uuid(); 786 | } 787 | 788 | private: 789 | void reset() 790 | { 791 | hasher.reset(); 792 | std::byte bytes[16]; 793 | auto nsbytes = nsuuid.as_bytes(); 794 | std::copy(std::cbegin(nsbytes), std::cend(nsbytes), bytes); 795 | hasher.process_bytes(bytes, 16); 796 | } 797 | 798 | template 799 | void process_characters(std::basic_string_view const str) 800 | { 801 | for (uint32_t c : str) 802 | { 803 | hasher.process_byte(static_cast(c & 0xFF)); 804 | if constexpr (!std::is_same_v) 805 | { 806 | hasher.process_byte(static_cast((c >> 8) & 0xFF)); 807 | hasher.process_byte(static_cast((c >> 16) & 0xFF)); 808 | hasher.process_byte(static_cast((c >> 24) & 0xFF)); 809 | } 810 | } 811 | } 812 | 813 | [[nodiscard]] uuid make_uuid() 814 | { 815 | detail::sha1::digest8_t digest; 816 | hasher.get_digest_bytes(digest); 817 | 818 | // variant must be 0b10xxxxxx 819 | digest[8] &= 0xBF; 820 | digest[8] |= 0x80; 821 | 822 | // version must be 0b0101xxxx 823 | digest[6] &= 0x5F; 824 | digest[6] |= 0x50; 825 | 826 | return uuid{ digest, digest + 16 }; 827 | } 828 | 829 | private: 830 | uuid nsuuid; 831 | detail::sha1 hasher; 832 | }; 833 | 834 | #ifdef UUID_TIME_GENERATOR 835 | // !!! DO NOT USE THIS IN PRODUCTION 836 | // this implementation is unreliable for good uuids 837 | class uuid_time_generator 838 | { 839 | using mac_address = std::array; 840 | 841 | std::optional device_address; 842 | 843 | [[nodiscard]] bool get_mac_address() 844 | { 845 | if (device_address.has_value()) 846 | { 847 | return true; 848 | } 849 | 850 | #ifdef _WIN32 851 | DWORD len = 0; 852 | auto ret = GetAdaptersInfo(nullptr, &len); 853 | if (ret != ERROR_BUFFER_OVERFLOW) return false; 854 | std::vector buf(len); 855 | auto pips = reinterpret_cast(&buf.front()); 856 | ret = GetAdaptersInfo(pips, &len); 857 | if (ret != ERROR_SUCCESS) return false; 858 | mac_address addr; 859 | std::copy(pips->Address, pips->Address + 6, std::begin(addr)); 860 | device_address = addr; 861 | #endif 862 | 863 | return device_address.has_value(); 864 | } 865 | 866 | [[nodiscard]] long long get_time_intervals() 867 | { 868 | auto start = std::chrono::system_clock::from_time_t(time_t(-12219292800)); 869 | auto diff = std::chrono::system_clock::now() - start; 870 | auto ns = std::chrono::duration_cast(diff).count(); 871 | return ns / 100; 872 | } 873 | 874 | [[nodiscard]] static unsigned short get_clock_sequence() 875 | { 876 | static std::mt19937 clock_gen(std::random_device{}()); 877 | static std::uniform_int_distribution clock_dis; 878 | static std::atomic_ushort clock_sequence = clock_dis(clock_gen); 879 | return clock_sequence++; 880 | } 881 | 882 | public: 883 | [[nodiscard]] uuid operator()() 884 | { 885 | if (get_mac_address()) 886 | { 887 | std::array data; 888 | 889 | auto tm = get_time_intervals(); 890 | 891 | auto clock_seq = get_clock_sequence(); 892 | 893 | auto ptm = reinterpret_cast(&tm); 894 | 895 | memcpy(&data[0], ptm + 4, 4); 896 | memcpy(&data[4], ptm + 2, 2); 897 | memcpy(&data[6], ptm, 2); 898 | 899 | memcpy(&data[8], &clock_seq, 2); 900 | 901 | // variant must be 0b10xxxxxx 902 | data[8] &= 0xBF; 903 | data[8] |= 0x80; 904 | 905 | // version must be 0b0001xxxx 906 | data[6] &= 0x1F; 907 | data[6] |= 0x10; 908 | 909 | memcpy(&data[10], &device_address.value()[0], 6); 910 | 911 | return uuids::uuid{std::cbegin(data), std::cend(data)}; 912 | } 913 | 914 | return {}; 915 | } 916 | }; 917 | #endif 918 | } 919 | 920 | namespace std 921 | { 922 | template <> 923 | struct hash 924 | { 925 | using argument_type = uuids::uuid; 926 | using result_type = std::size_t; 927 | 928 | [[nodiscard]] result_type operator()(argument_type const &uuid) const 929 | { 930 | #ifdef UUID_HASH_STRING_BASED 931 | std::hash hasher; 932 | return static_cast(hasher(uuids::to_string(uuid))); 933 | #else 934 | uint64_t l = 935 | static_cast(uuid.data[0]) << 56 | 936 | static_cast(uuid.data[1]) << 48 | 937 | static_cast(uuid.data[2]) << 40 | 938 | static_cast(uuid.data[3]) << 32 | 939 | static_cast(uuid.data[4]) << 24 | 940 | static_cast(uuid.data[5]) << 16 | 941 | static_cast(uuid.data[6]) << 8 | 942 | static_cast(uuid.data[7]); 943 | uint64_t h = 944 | static_cast(uuid.data[8]) << 56 | 945 | static_cast(uuid.data[9]) << 48 | 946 | static_cast(uuid.data[10]) << 40 | 947 | static_cast(uuid.data[11]) << 32 | 948 | static_cast(uuid.data[12]) << 24 | 949 | static_cast(uuid.data[13]) << 16 | 950 | static_cast(uuid.data[14]) << 8 | 951 | static_cast(uuid.data[15]); 952 | 953 | if constexpr (sizeof(result_type) > 4) 954 | { 955 | return result_type(l ^ h); 956 | } 957 | else 958 | { 959 | uint64_t hash64 = l ^ h; 960 | return result_type(uint32_t(hash64 >> 32) ^ uint32_t(hash64)); 961 | } 962 | #endif 963 | } 964 | }; 965 | } 966 | 967 | #endif /* STDUUID_H */ 968 | -------------------------------------------------------------------------------- /lgtm.yml: -------------------------------------------------------------------------------- 1 | path_classifiers: 2 | test: 3 | - "test" 4 | - exclude: "catch" 5 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Test target 2 | add_executable(test_${PROJECT_NAME} main.cpp test_generators.cpp test_uuid.cpp) 3 | target_include_directories(test_${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/catch) 4 | target_link_libraries(test_${PROJECT_NAME} PRIVATE ${PROJECT_NAME}) 5 | if (UUID_USING_CXX20_SPAN) 6 | set_target_properties(test_${PROJECT_NAME} PROPERTIES CXX_STANDARD 20) 7 | else () 8 | set_target_properties(test_${PROJECT_NAME} PROPERTIES CXX_STANDARD 17) 9 | endif () 10 | if (WIN32) 11 | target_compile_options(test_${PROJECT_NAME} PRIVATE /EHc /Zc:hiddenFriend) 12 | target_compile_definitions(test_${PROJECT_NAME} PRIVATE _SCL_SECURE_NO_WARNINGS) 13 | elseif (APPLE) 14 | target_compile_options(test_${PROJECT_NAME} PRIVATE -fexceptions -g -Wall) 15 | else () 16 | target_compile_options(test_${PROJECT_NAME} PRIVATE -fexceptions -g -Wall) 17 | endif () 18 | get_target_property(CURRENT_COMPILE_OPTIONS test_${PROJECT_NAME} COMPILE_OPTIONS) 19 | message(STATUS "** ${CMAKE_CXX_COMPILER_ID} flags: ${CURRENT_COMPILE_OPTIONS}") 20 | 21 | # Tests 22 | add_test(NAME "test_${PROJECT_NAME}" COMMAND "test_${PROJECT_NAME}" "-r compact") 23 | set_tests_properties("test_${PROJECT_NAME}" 24 | PROPERTIES 25 | PASS_REGULAR_EXPRESSION "Passed all.*") 26 | set_tests_properties("test_${PROJECT_NAME}" 27 | PROPERTIES 28 | FAIL_REGULAR_EXPRESSION "Failed \\d+ test cases") 29 | set_tests_properties("test_${PROJECT_NAME}" 30 | PROPERTIES 31 | TIMEOUT 120) 32 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "catch.hpp" 3 | 4 | -------------------------------------------------------------------------------- /test/test_generators.cpp: -------------------------------------------------------------------------------- 1 | #include "uuid.h" 2 | #include "catch.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace uuids; 10 | 11 | TEST_CASE("Test multiple default generators", "[gen][rand]") 12 | { 13 | uuid id1; 14 | uuid id2; 15 | 16 | { 17 | std::random_device rd; 18 | auto seed_data = std::array {}; 19 | std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); 20 | std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); 21 | std::mt19937 generator(seq); 22 | 23 | id1 = uuids::uuid_random_generator{ generator }(); 24 | REQUIRE(!id1.is_nil()); 25 | REQUIRE(id1.version() == uuids::uuid_version::random_number_based); 26 | REQUIRE(id1.variant() == uuids::uuid_variant::rfc); 27 | } 28 | 29 | { 30 | std::random_device rd; 31 | auto seed_data = std::array {}; 32 | std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); 33 | std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); 34 | std::mt19937 generator(seq); 35 | 36 | id2 = uuids::uuid_random_generator{ generator }(); 37 | REQUIRE(!id2.is_nil()); 38 | REQUIRE(id2.version() == uuids::uuid_version::random_number_based); 39 | REQUIRE(id2.variant() == uuids::uuid_variant::rfc); 40 | } 41 | 42 | REQUIRE(id1 != id2); 43 | } 44 | 45 | TEST_CASE("Test default generator", "[gen][rand]") 46 | { 47 | std::random_device rd; 48 | auto seed_data = std::array {}; 49 | std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); 50 | std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); 51 | std::mt19937 generator(seq); 52 | 53 | uuid const guid = uuids::uuid_random_generator{generator}(); 54 | REQUIRE(!guid.is_nil()); 55 | REQUIRE(guid.version() == uuids::uuid_version::random_number_based); 56 | REQUIRE(guid.variant() == uuids::uuid_variant::rfc); 57 | } 58 | 59 | TEST_CASE("Test random generator (conversion ctor w/ smart ptr)", "[gen][rand]") 60 | { 61 | std::random_device rd; 62 | auto seed_data = std::array {}; 63 | std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); 64 | std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); 65 | std::mt19937 generator(seq); 66 | 67 | uuids::uuid_random_generator dgen(&generator); 68 | auto id1 = dgen(); 69 | REQUIRE(!id1.is_nil()); 70 | REQUIRE(id1.version() == uuids::uuid_version::random_number_based); 71 | REQUIRE(id1.variant() == uuids::uuid_variant::rfc); 72 | 73 | auto id2 = dgen(); 74 | REQUIRE(!id2.is_nil()); 75 | REQUIRE(id2.version() == uuids::uuid_version::random_number_based); 76 | REQUIRE(id2.variant() == uuids::uuid_variant::rfc); 77 | 78 | REQUIRE(id1 != id2); 79 | } 80 | 81 | TEST_CASE("Test random generator (conversion ctor w/ ptr)", "[gen][rand]") 82 | { 83 | std::random_device rd; 84 | auto seed_data = std::array {}; 85 | std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); 86 | std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); 87 | auto generator = std::make_unique(seq); 88 | 89 | uuids::uuid_random_generator dgen(generator.get()); 90 | auto id1 = dgen(); 91 | REQUIRE(!id1.is_nil()); 92 | REQUIRE(id1.version() == uuids::uuid_version::random_number_based); 93 | REQUIRE(id1.variant() == uuids::uuid_variant::rfc); 94 | 95 | auto id2 = dgen(); 96 | REQUIRE(!id1.is_nil()); 97 | REQUIRE(id2.version() == uuids::uuid_version::random_number_based); 98 | REQUIRE(id2.variant() == uuids::uuid_variant::rfc); 99 | 100 | REQUIRE(id1 != id2); 101 | } 102 | 103 | TEST_CASE("Test random generator (conversion ctor w/ ref)", "[gen][rand]") 104 | { 105 | std::random_device rd; 106 | auto seed_data = std::array {}; 107 | std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); 108 | std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); 109 | std::mt19937 generator(seq); 110 | 111 | uuids::uuid_random_generator dgen(generator); 112 | auto id1 = dgen(); 113 | REQUIRE(!id1.is_nil()); 114 | REQUIRE(id1.version() == uuids::uuid_version::random_number_based); 115 | REQUIRE(id1.variant() == uuids::uuid_variant::rfc); 116 | 117 | auto id2 = dgen(); 118 | REQUIRE(!id2.is_nil()); 119 | REQUIRE(id2.version() == uuids::uuid_version::random_number_based); 120 | REQUIRE(id2.variant() == uuids::uuid_variant::rfc); 121 | 122 | REQUIRE(id1 != id2); 123 | } 124 | 125 | TEST_CASE("Test basic random generator (conversion ctor w/ ptr) w/ ranlux48_base", "[gen][rand]") 126 | { 127 | std::random_device rd; 128 | auto seed_data = std::array {}; 129 | std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); 130 | std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); 131 | std::ranlux48_base generator(seq); 132 | 133 | uuids::basic_uuid_random_generator dgen(&generator); 134 | auto id1 = dgen(); 135 | REQUIRE(!id1.is_nil()); 136 | REQUIRE(id1.version() == uuids::uuid_version::random_number_based); 137 | REQUIRE(id1.variant() == uuids::uuid_variant::rfc); 138 | 139 | auto id2 = dgen(); 140 | REQUIRE(!id2.is_nil()); 141 | REQUIRE(id2.version() == uuids::uuid_version::random_number_based); 142 | REQUIRE(id2.variant() == uuids::uuid_variant::rfc); 143 | 144 | REQUIRE(id1 != id2); 145 | } 146 | 147 | TEST_CASE("Test basic random generator (conversion ctor w/ smart ptr) w/ ranlux48_base", "[gen][rand]") 148 | { 149 | std::random_device rd; 150 | auto seed_data = std::array {}; 151 | std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); 152 | std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); 153 | auto generator = std::make_unique(seq); 154 | 155 | uuids::basic_uuid_random_generator dgen(generator.get()); 156 | auto id1 = dgen(); 157 | REQUIRE(!id1.is_nil()); 158 | REQUIRE(id1.version() == uuids::uuid_version::random_number_based); 159 | REQUIRE(id1.variant() == uuids::uuid_variant::rfc); 160 | 161 | auto id2 = dgen(); 162 | REQUIRE(!id2.is_nil()); 163 | REQUIRE(id2.version() == uuids::uuid_version::random_number_based); 164 | REQUIRE(id2.variant() == uuids::uuid_variant::rfc); 165 | 166 | REQUIRE(id1 != id2); 167 | } 168 | 169 | TEST_CASE("Test basic random generator (conversion ctor w/ ref) w/ ranlux48_base", "[gen][rand]") 170 | { 171 | std::random_device rd; 172 | auto seed_data = std::array {}; 173 | std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); 174 | std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); 175 | std::ranlux48_base generator(seq); 176 | 177 | uuids::basic_uuid_random_generator dgen(generator); 178 | auto id1 = dgen(); 179 | REQUIRE(!id1.is_nil()); 180 | REQUIRE(id1.version() == uuids::uuid_version::random_number_based); 181 | REQUIRE(id1.variant() == uuids::uuid_variant::rfc); 182 | 183 | auto id2 = dgen(); 184 | REQUIRE(!id2.is_nil()); 185 | REQUIRE(id2.version() == uuids::uuid_version::random_number_based); 186 | REQUIRE(id2.variant() == uuids::uuid_variant::rfc); 187 | 188 | REQUIRE(id1 != id2); 189 | } 190 | 191 | TEST_CASE("Test namespaces", "[gen][name]") 192 | { 193 | REQUIRE(uuid_namespace_dns == uuids::uuid::from_string("6ba7b810-9dad-11d1-80b4-00c04fd430c8")); 194 | REQUIRE(uuid_namespace_url == uuids::uuid::from_string("6ba7b811-9dad-11d1-80b4-00c04fd430c8")); 195 | REQUIRE(uuid_namespace_oid == uuids::uuid::from_string("6ba7b812-9dad-11d1-80b4-00c04fd430c8")); 196 | REQUIRE(uuid_namespace_x500 == uuids::uuid::from_string("6ba7b814-9dad-11d1-80b4-00c04fd430c8")); 197 | } 198 | 199 | TEST_CASE("Test name generator (char*)", "[gen][name]") 200 | { 201 | uuids::uuid_name_generator dgen(uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()); 202 | auto id1 = dgen("john"); 203 | REQUIRE(!id1.is_nil()); 204 | REQUIRE(id1.version() == uuids::uuid_version::name_based_sha1); 205 | REQUIRE(id1.variant() == uuids::uuid_variant::rfc); 206 | 207 | auto id2 = dgen("jane"); 208 | REQUIRE(!id2.is_nil()); 209 | REQUIRE(id2.version() == uuids::uuid_version::name_based_sha1); 210 | REQUIRE(id2.variant() == uuids::uuid_variant::rfc); 211 | 212 | auto id3 = dgen("jane"); 213 | REQUIRE(!id3.is_nil()); 214 | REQUIRE(id3.version() == uuids::uuid_version::name_based_sha1); 215 | REQUIRE(id3.variant() == uuids::uuid_variant::rfc); 216 | 217 | auto id4 = dgen(L"jane"); 218 | REQUIRE(!id4.is_nil()); 219 | REQUIRE(id4.version() == uuids::uuid_version::name_based_sha1); 220 | REQUIRE(id4.variant() == uuids::uuid_variant::rfc); 221 | 222 | REQUIRE(id1 != id2); 223 | REQUIRE(id2 == id3); 224 | REQUIRE(id3 != id4); 225 | } 226 | 227 | TEST_CASE("Test name generator (std::string)", "[gen][name]") 228 | { 229 | using namespace std::string_literals; 230 | 231 | uuids::uuid_name_generator dgen(uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()); 232 | auto id1 = dgen("john"s); 233 | REQUIRE(!id1.is_nil()); 234 | REQUIRE(id1.version() == uuids::uuid_version::name_based_sha1); 235 | REQUIRE(id1.variant() == uuids::uuid_variant::rfc); 236 | 237 | auto id2 = dgen("jane"s); 238 | REQUIRE(!id2.is_nil()); 239 | REQUIRE(id2.version() == uuids::uuid_version::name_based_sha1); 240 | REQUIRE(id2.variant() == uuids::uuid_variant::rfc); 241 | 242 | auto id3 = dgen("jane"s); 243 | REQUIRE(!id3.is_nil()); 244 | REQUIRE(id3.version() == uuids::uuid_version::name_based_sha1); 245 | REQUIRE(id3.variant() == uuids::uuid_variant::rfc); 246 | 247 | auto id4 = dgen(L"jane"s); 248 | REQUIRE(!id4.is_nil()); 249 | REQUIRE(id4.version() == uuids::uuid_version::name_based_sha1); 250 | REQUIRE(id4.variant() == uuids::uuid_variant::rfc); 251 | 252 | REQUIRE(id1 != id2); 253 | REQUIRE(id2 == id3); 254 | REQUIRE(id3 != id4); 255 | } 256 | 257 | TEST_CASE("Test name generator (std::string_view)", "[gen][name]") 258 | { 259 | using namespace std::string_view_literals; 260 | 261 | uuids::uuid_name_generator dgen(uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()); 262 | auto id1 = dgen("john"sv); 263 | REQUIRE(!id1.is_nil()); 264 | REQUIRE(id1.version() == uuids::uuid_version::name_based_sha1); 265 | REQUIRE(id1.variant() == uuids::uuid_variant::rfc); 266 | 267 | auto id2 = dgen("jane"sv); 268 | REQUIRE(!id2.is_nil()); 269 | REQUIRE(id2.version() == uuids::uuid_version::name_based_sha1); 270 | REQUIRE(id2.variant() == uuids::uuid_variant::rfc); 271 | 272 | auto id3 = dgen("jane"sv); 273 | REQUIRE(!id3.is_nil()); 274 | REQUIRE(id3.version() == uuids::uuid_version::name_based_sha1); 275 | REQUIRE(id3.variant() == uuids::uuid_variant::rfc); 276 | 277 | auto id4 = dgen(L"jane"sv); 278 | REQUIRE(!id4.is_nil()); 279 | REQUIRE(id4.version() == uuids::uuid_version::name_based_sha1); 280 | REQUIRE(id4.variant() == uuids::uuid_variant::rfc); 281 | 282 | REQUIRE(id1 != id2); 283 | REQUIRE(id2 == id3); 284 | REQUIRE(id3 != id4); 285 | } 286 | 287 | TEST_CASE("Test name generator equality (char const*, std::string, std::string_view)", "[gen][name]") 288 | { 289 | using namespace std::literals; 290 | 291 | uuids::uuid_name_generator dgen(uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()); 292 | auto id1 = dgen("john"); 293 | auto id2 = dgen("john"s); 294 | auto id3 = dgen("john"sv); 295 | 296 | REQUIRE(id1 == id2); 297 | REQUIRE(id2 == id3); 298 | } 299 | 300 | #ifdef UUID_TIME_GENERATOR 301 | TEST_CASE("Test time generator", "[gen][time]") 302 | { 303 | uuid_time_generator gen; 304 | auto id1 = gen(); 305 | auto id2 = gen(); 306 | REQUIRE(!id1.is_nil()); 307 | REQUIRE(id1.variant() == uuids::uuid_variant::rfc); 308 | REQUIRE(id1.version() == uuids::uuid_version::time_based); 309 | 310 | REQUIRE(!id2.is_nil()); 311 | REQUIRE(id2.variant() == uuids::uuid_variant::rfc); 312 | REQUIRE(id2.version() == uuids::uuid_version::time_based); 313 | 314 | REQUIRE(id1 != id2); 315 | 316 | std::set ids; 317 | for (int i = 0; i < 100; ++i) 318 | ids.insert(gen()); 319 | 320 | REQUIRE(ids.size() == 100); 321 | } 322 | #endif -------------------------------------------------------------------------------- /test/test_uuid.cpp: -------------------------------------------------------------------------------- 1 | #include "uuid.h" 2 | #include "catch.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace uuids; 11 | 12 | namespace 13 | { 14 | // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0205r0.html 15 | template 16 | void seed_rng(EngineT& engine) 17 | { 18 | using engine_type = typename EngineT::result_type; 19 | using device_type = std::random_device::result_type; 20 | using seedseq_type = std::seed_seq::result_type; 21 | constexpr auto bytes_needed = StateSize * sizeof(engine_type); 22 | constexpr auto numbers_needed = (sizeof(device_type) < sizeof(seedseq_type)) 23 | ? (bytes_needed / sizeof(device_type)) 24 | : (bytes_needed / sizeof(seedseq_type)); 25 | std::array numbers{}; 26 | std::random_device rnddev{}; 27 | std::generate(std::begin(numbers), std::end(numbers), std::ref(rnddev)); 28 | std::seed_seq seedseq(std::cbegin(numbers), std::cend(numbers)); 29 | engine.seed(seedseq); 30 | } 31 | } 32 | 33 | TEST_CASE("Test default constructor", "[ctors]") 34 | { 35 | uuid empty; 36 | REQUIRE(empty.is_nil()); 37 | } 38 | 39 | TEST_CASE("Test string conversion", "[ops]") 40 | { 41 | uuid empty; 42 | REQUIRE(uuids::to_string(empty) == "00000000-0000-0000-0000-000000000000"); 43 | REQUIRE(uuids::to_string(empty) == L"00000000-0000-0000-0000-000000000000"); 44 | } 45 | 46 | TEST_CASE("Test is_valid_uuid(char*)", "[parse]") 47 | { 48 | REQUIRE(uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43")); 49 | REQUIRE(uuids::uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43}")); 50 | REQUIRE(uuids::uuid::is_valid_uuid(L"47183823-2574-4bfd-b411-99ed177d3e43")); 51 | REQUIRE(uuids::uuid::is_valid_uuid(L"{47183823-2574-4bfd-b411-99ed177d3e43}")); 52 | REQUIRE(uuids::uuid::is_valid_uuid("00000000-0000-0000-0000-000000000000")); 53 | REQUIRE(uuids::uuid::is_valid_uuid("{00000000-0000-0000-0000-000000000000}")); 54 | REQUIRE(uuids::uuid::is_valid_uuid(L"00000000-0000-0000-0000-000000000000")); 55 | REQUIRE(uuids::uuid::is_valid_uuid(L"{00000000-0000-0000-0000-000000000000}")); 56 | } 57 | 58 | TEST_CASE("Test is_valid_uuid(basic_string)", "[parse]") 59 | { 60 | using namespace std::string_literals; 61 | 62 | { 63 | auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; 64 | REQUIRE(uuids::uuid::is_valid_uuid(str)); 65 | } 66 | 67 | { 68 | auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"s; 69 | REQUIRE(uuids::uuid::is_valid_uuid(str)); 70 | } 71 | 72 | { 73 | auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s; 74 | REQUIRE(uuids::uuid::is_valid_uuid(str)); 75 | } 76 | 77 | { 78 | auto str = L"{47183823-2574-4bfd-b411-99ed177d3e43}"s; 79 | REQUIRE(uuids::uuid::is_valid_uuid(str)); 80 | } 81 | 82 | { 83 | auto str = "00000000-0000-0000-0000-000000000000"s; 84 | REQUIRE(uuids::uuid::is_valid_uuid(str)); 85 | } 86 | 87 | { 88 | auto str = "{00000000-0000-0000-0000-000000000000}"s; 89 | REQUIRE(uuids::uuid::is_valid_uuid(str)); 90 | } 91 | 92 | { 93 | auto str = L"00000000-0000-0000-0000-000000000000"s; 94 | REQUIRE(uuids::uuid::is_valid_uuid(str)); 95 | } 96 | 97 | { 98 | auto str = L"{00000000-0000-0000-0000-000000000000}"s; 99 | REQUIRE(uuids::uuid::is_valid_uuid(str)); 100 | } 101 | } 102 | 103 | TEST_CASE("Test is_valid_uuid(basic_string_view)", "[parse]") 104 | { 105 | using namespace std::string_view_literals; 106 | 107 | REQUIRE(uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43"sv)); 108 | REQUIRE(uuids::uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43}"sv)); 109 | REQUIRE(uuids::uuid::is_valid_uuid(L"47183823-2574-4bfd-b411-99ed177d3e43"sv)); 110 | REQUIRE(uuids::uuid::is_valid_uuid(L"{47183823-2574-4bfd-b411-99ed177d3e43}"sv)); 111 | REQUIRE(uuids::uuid::is_valid_uuid("00000000-0000-0000-0000-000000000000"sv)); 112 | REQUIRE(uuids::uuid::is_valid_uuid("{00000000-0000-0000-0000-000000000000}"sv)); 113 | REQUIRE(uuids::uuid::is_valid_uuid(L"00000000-0000-0000-0000-000000000000"sv)); 114 | REQUIRE(uuids::uuid::is_valid_uuid(L"{00000000-0000-0000-0000-000000000000}"sv)); 115 | } 116 | 117 | TEST_CASE("Test is_valid_uuid(char*) invalid format", "[parse]") 118 | { 119 | REQUIRE(!uuids::uuid::is_valid_uuid("")); 120 | REQUIRE(!uuids::uuid::is_valid_uuid("{}")); 121 | REQUIRE(!uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e4")); 122 | REQUIRE(!uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e430")); 123 | REQUIRE(!uuids::uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43")); 124 | REQUIRE(!uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43}")); 125 | } 126 | 127 | TEST_CASE("Test is_valid_uuid(basic_string) invalid format", "[parse]") 128 | { 129 | using namespace std::string_literals; 130 | 131 | { 132 | auto str = ""s; 133 | REQUIRE(!uuids::uuid::is_valid_uuid(str)); 134 | } 135 | 136 | { 137 | auto str = "{}"s; 138 | REQUIRE(!uuids::uuid::is_valid_uuid(str)); 139 | } 140 | 141 | { 142 | auto str = "47183823-2574-4bfd-b411-99ed177d3e4"s; 143 | REQUIRE(!uuids::uuid::is_valid_uuid(str)); 144 | } 145 | 146 | { 147 | auto str = "47183823-2574-4bfd-b411-99ed177d3e430"s; 148 | REQUIRE(!uuids::uuid::is_valid_uuid(str)); 149 | } 150 | 151 | { 152 | auto str = "{47183823-2574-4bfd-b411-99ed177d3e43"s; 153 | REQUIRE(!uuids::uuid::is_valid_uuid(str)); 154 | } 155 | 156 | { 157 | auto str = "47183823-2574-4bfd-b411-99ed177d3e43}"s; 158 | REQUIRE(!uuids::uuid::is_valid_uuid(str)); 159 | } 160 | } 161 | 162 | TEST_CASE("Test is_valid_uuid(basic_string_view) invalid format", "[parse]") 163 | { 164 | using namespace std::string_view_literals; 165 | 166 | REQUIRE(!uuids::uuid::is_valid_uuid(""sv)); 167 | REQUIRE(!uuids::uuid::is_valid_uuid("{}"sv)); 168 | REQUIRE(!uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e4"sv)); 169 | REQUIRE(!uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e430"sv)); 170 | REQUIRE(!uuids::uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43"sv)); 171 | REQUIRE(!uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43}"sv)); 172 | } 173 | 174 | TEST_CASE("Test from_string(char*)", "[parse]") 175 | { 176 | { 177 | auto str = "47183823-2574-4bfd-b411-99ed177d3e43"; 178 | auto guid = uuids::uuid::from_string(str).value(); 179 | REQUIRE(uuids::to_string(guid) == str); 180 | } 181 | 182 | { 183 | auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"; 184 | auto guid = uuids::uuid::from_string(str).value(); 185 | REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); 186 | } 187 | 188 | { 189 | auto guid = uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value(); 190 | REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); 191 | REQUIRE(uuids::to_string(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43"); 192 | } 193 | 194 | { 195 | auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"; 196 | auto guid = uuids::uuid::from_string(str).value(); 197 | REQUIRE(uuids::to_string(guid) == str); 198 | } 199 | 200 | { 201 | auto str = "4718382325744bfdb41199ed177d3e43"; 202 | REQUIRE_NOTHROW(uuids::uuid::from_string(str)); 203 | REQUIRE(uuids::uuid::from_string(str).has_value()); 204 | } 205 | 206 | { 207 | auto str = "00000000-0000-0000-0000-000000000000"; 208 | auto guid = uuids::uuid::from_string(str).value(); 209 | REQUIRE(guid.is_nil()); 210 | } 211 | 212 | { 213 | auto str = "{00000000-0000-0000-0000-000000000000}"; 214 | auto guid = uuids::uuid::from_string(str).value(); 215 | REQUIRE(guid.is_nil()); 216 | } 217 | 218 | { 219 | auto str = L"00000000-0000-0000-0000-000000000000"; 220 | auto guid = uuids::uuid::from_string(str).value(); 221 | REQUIRE(guid.is_nil()); 222 | } 223 | 224 | { 225 | auto str = L"{00000000-0000-0000-0000-000000000000}"; 226 | auto guid = uuids::uuid::from_string(str).value(); 227 | REQUIRE(guid.is_nil()); 228 | } 229 | } 230 | 231 | TEST_CASE("Test from_string(basic_string)", "[parse]") 232 | { 233 | using namespace std::string_literals; 234 | 235 | { 236 | auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; 237 | auto guid = uuids::uuid::from_string(str).value(); 238 | REQUIRE(uuids::to_string(guid) == str); 239 | } 240 | 241 | { 242 | auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"s; 243 | auto guid = uuids::uuid::from_string(str).value(); 244 | REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); 245 | } 246 | 247 | { 248 | auto guid = uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43"s).value(); 249 | REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); 250 | REQUIRE(uuids::to_string(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43"); 251 | } 252 | 253 | { 254 | auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s; 255 | auto guid = uuids::uuid::from_string(str).value(); 256 | REQUIRE(uuids::to_string(guid) == str); 257 | } 258 | 259 | { 260 | auto str = "4718382325744bfdb41199ed177d3e43"s; 261 | REQUIRE_NOTHROW(uuids::uuid::from_string(str)); 262 | REQUIRE(uuids::uuid::from_string(str).has_value()); 263 | } 264 | 265 | { 266 | auto str = "00000000-0000-0000-0000-000000000000"s; 267 | auto guid = uuids::uuid::from_string(str).value(); 268 | REQUIRE(guid.is_nil()); 269 | } 270 | 271 | { 272 | auto str = "{00000000-0000-0000-0000-000000000000}"s; 273 | auto guid = uuids::uuid::from_string(str).value(); 274 | REQUIRE(guid.is_nil()); 275 | } 276 | 277 | { 278 | auto str = L"00000000-0000-0000-0000-000000000000"s; 279 | auto guid = uuids::uuid::from_string(str).value(); 280 | REQUIRE(guid.is_nil()); 281 | } 282 | 283 | { 284 | auto str = L"{00000000-0000-0000-0000-000000000000}"s; 285 | auto guid = uuids::uuid::from_string(str).value(); 286 | REQUIRE(guid.is_nil()); 287 | } 288 | } 289 | 290 | TEST_CASE("Test from_string(basic_string_view)", "[parse]") 291 | { 292 | using namespace std::string_view_literals; 293 | 294 | { 295 | auto str = "47183823-2574-4bfd-b411-99ed177d3e43"sv; 296 | auto guid = uuids::uuid::from_string(str).value(); 297 | REQUIRE(uuids::to_string(guid) == str); 298 | } 299 | 300 | { 301 | auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"sv; 302 | auto guid = uuids::uuid::from_string(str).value(); 303 | REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); 304 | } 305 | 306 | { 307 | auto guid = uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43"sv).value(); 308 | REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); 309 | REQUIRE(uuids::to_string(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43"); 310 | } 311 | 312 | { 313 | auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"sv; 314 | auto guid = uuids::uuid::from_string(str).value(); 315 | REQUIRE(uuids::to_string(guid) == str); 316 | } 317 | 318 | { 319 | auto str = "4718382325744bfdb41199ed177d3e43"sv; 320 | REQUIRE_NOTHROW(uuids::uuid::from_string(str)); 321 | REQUIRE(uuids::uuid::from_string(str).has_value()); 322 | } 323 | 324 | { 325 | auto str = "00000000-0000-0000-0000-000000000000"sv; 326 | auto guid = uuids::uuid::from_string(str).value(); 327 | REQUIRE(guid.is_nil()); 328 | } 329 | 330 | { 331 | auto str = "{00000000-0000-0000-0000-000000000000}"sv; 332 | auto guid = uuids::uuid::from_string(str).value(); 333 | REQUIRE(guid.is_nil()); 334 | } 335 | 336 | { 337 | auto str = L"00000000-0000-0000-0000-000000000000"sv; 338 | auto guid = uuids::uuid::from_string(str).value(); 339 | REQUIRE(guid.is_nil()); 340 | } 341 | 342 | { 343 | auto str = L"{00000000-0000-0000-0000-000000000000}"sv; 344 | auto guid = uuids::uuid::from_string(str).value(); 345 | REQUIRE(guid.is_nil()); 346 | } 347 | } 348 | 349 | TEST_CASE("Test constexpr from_string", "[const]") 350 | { 351 | constexpr uuid value = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value(); 352 | static_assert(!value.is_nil()); 353 | static_assert(value.variant() == uuid_variant::rfc); 354 | static_assert(value.version() != uuid_version::none); 355 | } 356 | 357 | TEST_CASE("Test from_string(char*) invalid format", "[parse]") 358 | { 359 | REQUIRE(!uuids::uuid::from_string("").has_value()); 360 | REQUIRE(!uuids::uuid::from_string("{}").has_value()); 361 | REQUIRE(!uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e4").has_value()); 362 | REQUIRE(!uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e430").has_value()); 363 | REQUIRE(!uuids::uuid::from_string("{47183823-2574-4bfd-b411-99ed177d3e43").has_value()); 364 | REQUIRE(!uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43}").has_value()); 365 | } 366 | 367 | TEST_CASE("Test from_string(basic_string) invalid format", "[parse]") 368 | { 369 | using namespace std::string_literals; 370 | 371 | { 372 | auto str = ""s; 373 | REQUIRE(!uuids::uuid::from_string(str).has_value()); 374 | } 375 | 376 | { 377 | auto str = "{}"s; 378 | REQUIRE(!uuids::uuid::from_string(str).has_value()); 379 | } 380 | 381 | { 382 | auto str = "47183823-2574-4bfd-b411-99ed177d3e4"s; 383 | REQUIRE(!uuids::uuid::from_string(str).has_value()); 384 | } 385 | 386 | { 387 | auto str = "47183823-2574-4bfd-b411-99ed177d3e430"s; 388 | REQUIRE(!uuids::uuid::from_string(str).has_value()); 389 | } 390 | 391 | { 392 | auto str = "{47183823-2574-4bfd-b411-99ed177d3e43"s; 393 | REQUIRE(!uuids::uuid::from_string(str).has_value()); 394 | } 395 | 396 | { 397 | auto str = "47183823-2574-4bfd-b411-99ed177d3e43}"s; 398 | REQUIRE(!uuids::uuid::from_string(str).has_value()); 399 | } 400 | } 401 | 402 | TEST_CASE("Test from_string(basic_string_view) invalid format", "[parse]") 403 | { 404 | using namespace std::string_view_literals; 405 | 406 | REQUIRE(!uuids::uuid::from_string(""sv).has_value()); 407 | REQUIRE(!uuids::uuid::from_string("{}"sv).has_value()); 408 | REQUIRE(!uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e4"sv).has_value()); 409 | REQUIRE(!uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e430"sv).has_value()); 410 | REQUIRE(!uuids::uuid::from_string("{47183823-2574-4bfd-b411-99ed177d3e43"sv).has_value()); 411 | REQUIRE(!uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43}"sv).has_value()); 412 | } 413 | 414 | TEST_CASE("Test iterators constructor", "[ctors]") 415 | { 416 | using namespace std::string_literals; 417 | 418 | { 419 | std::array arr{ { 420 | 0x47, 0x18, 0x38, 0x23, 421 | 0x25, 0x74, 422 | 0x4b, 0xfd, 423 | 0xb4, 0x11, 424 | 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 } }; 425 | 426 | uuid guid(std::begin(arr), std::end(arr)); 427 | REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"s); 428 | } 429 | 430 | { 431 | uuids::uuid::value_type arr[16] = { 432 | 0x47, 0x18, 0x38, 0x23, 433 | 0x25, 0x74, 434 | 0x4b, 0xfd, 435 | 0xb4, 0x11, 436 | 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 }; 437 | 438 | uuid guid(std::begin(arr), std::end(arr)); 439 | REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"s); 440 | } 441 | } 442 | 443 | TEST_CASE("Test array constructors", "[ctors]") 444 | { 445 | using namespace std::string_literals; 446 | 447 | { 448 | uuids::uuid guid{ 449 | {0x47, 0x18, 0x38, 0x23, 450 | 0x25, 0x74, 451 | 0x4b, 0xfd, 452 | 0xb4, 0x11, 453 | 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 } }; 454 | 455 | REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"s); 456 | } 457 | 458 | { 459 | std::array arr{ { 460 | 0x47, 0x18, 0x38, 0x23, 461 | 0x25, 0x74, 462 | 0x4b, 0xfd, 463 | 0xb4, 0x11, 464 | 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 } }; 465 | 466 | uuid guid(arr); 467 | REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"s); 468 | } 469 | 470 | { 471 | uuids::uuid::value_type arr[16] { 472 | 0x47, 0x18, 0x38, 0x23, 473 | 0x25, 0x74, 474 | 0x4b, 0xfd, 475 | 0xb4, 0x11, 476 | 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 }; 477 | 478 | uuid guid(arr); 479 | REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"s); 480 | } 481 | } 482 | 483 | TEST_CASE("Test equality", "[operators]") 484 | { 485 | uuid empty; 486 | 487 | auto engine = uuids::uuid_random_generator::engine_type{}; 488 | seed_rng(engine); 489 | uuid guid = uuids::uuid_random_generator{engine}(); 490 | 491 | REQUIRE(empty == empty); 492 | REQUIRE(guid == guid); 493 | REQUIRE(empty != guid); 494 | } 495 | 496 | TEST_CASE("Test comparison", "[operators]") 497 | { 498 | auto empty = uuid{}; 499 | 500 | auto engine = uuids::uuid_random_generator::engine_type{}; 501 | seed_rng(engine); 502 | 503 | uuids::uuid_random_generator gen{ engine }; 504 | auto id = gen(); 505 | 506 | REQUIRE(empty < id); 507 | 508 | std::set ids{ 509 | uuid{}, 510 | gen(), 511 | gen(), 512 | gen(), 513 | gen() 514 | }; 515 | 516 | REQUIRE(ids.size() == 5); 517 | REQUIRE(ids.find(uuid{}) != ids.end()); 518 | } 519 | 520 | TEST_CASE("Test hashing", "[ops]") 521 | { 522 | using namespace std::string_literals; 523 | auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; 524 | auto guid = uuids::uuid::from_string(str).value(); 525 | 526 | auto h1 = std::hash{}; 527 | auto h2 = std::hash{}; 528 | #ifdef UUID_HASH_STRING_BASED 529 | REQUIRE(h1(str) == h2(guid)); 530 | #else 531 | REQUIRE(h1(str) != h2(guid)); 532 | #endif 533 | 534 | auto engine = uuids::uuid_random_generator::engine_type{}; 535 | seed_rng(engine); 536 | uuids::uuid_random_generator gen{ engine }; 537 | 538 | std::unordered_set ids{ 539 | uuid{}, 540 | gen(), 541 | gen(), 542 | gen(), 543 | gen() 544 | }; 545 | 546 | REQUIRE(ids.size() == 5); 547 | REQUIRE(ids.find(uuid{}) != ids.end()); 548 | } 549 | 550 | TEST_CASE("Test swap", "[ops]") 551 | { 552 | uuid empty; 553 | 554 | auto engine = uuids::uuid_random_generator::engine_type{}; 555 | seed_rng(engine); 556 | uuid guid = uuids::uuid_random_generator{engine}(); 557 | 558 | REQUIRE(empty.is_nil()); 559 | REQUIRE(!guid.is_nil()); 560 | 561 | std::swap(empty, guid); 562 | 563 | REQUIRE(!empty.is_nil()); 564 | REQUIRE(guid.is_nil()); 565 | 566 | empty.swap(guid); 567 | 568 | REQUIRE(empty.is_nil()); 569 | REQUIRE(!guid.is_nil()); 570 | } 571 | 572 | TEST_CASE("Test constexpr", "[const]") 573 | { 574 | constexpr uuid empty; 575 | static_assert(empty.is_nil()); 576 | static_assert(empty.variant() == uuid_variant::ncs); 577 | static_assert(empty.version() == uuid_version::none); 578 | } 579 | 580 | TEST_CASE("Test size", "[operators]") 581 | { 582 | REQUIRE(sizeof(uuid) == 16); 583 | } 584 | 585 | TEST_CASE("Test assignment", "[ops]") 586 | { 587 | auto id1 = uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value(); 588 | auto id2 = id1; 589 | REQUIRE(id1 == id2); 590 | 591 | id1 = uuids::uuid::from_string("{fea43102-064f-4444-adc2-02cec42623f8}").value(); 592 | REQUIRE(id1 != id2); 593 | 594 | auto id3 = std::move(id2); 595 | REQUIRE(uuids::to_string(id3) == "47183823-2574-4bfd-b411-99ed177d3e43"); 596 | } 597 | 598 | TEST_CASE("Test trivial", "[trivial]") 599 | { 600 | REQUIRE(std::is_trivially_copyable_v); 601 | } 602 | 603 | TEST_CASE("Test as_bytes", "[ops]") 604 | { 605 | std::array arr{ { 606 | 0x47, 0x18, 0x38, 0x23, 607 | 0x25, 0x74, 608 | 0x4b, 0xfd, 609 | 0xb4, 0x11, 610 | 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 611 | } }; 612 | 613 | { 614 | uuids::uuid id{ arr }; 615 | REQUIRE(!id.is_nil()); 616 | 617 | auto view = id.as_bytes(); 618 | REQUIRE(memcmp(view.data(), arr.data(), arr.size()) == 0); 619 | } 620 | 621 | { 622 | const uuids::uuid id{ arr }; 623 | REQUIRE(!id.is_nil()); 624 | 625 | auto view = id.as_bytes(); 626 | REQUIRE(memcmp(view.data(), arr.data(), arr.size()) == 0); 627 | } 628 | 629 | } 630 | --------------------------------------------------------------------------------