Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3afe7193fa | ||
|
|
e5da05d8de | ||
|
|
30b95f9c8f | ||
|
|
3dca9d816c | ||
|
|
d1f0f7f0ba | ||
|
|
cd9ca5e7d7 | ||
|
|
dfe3d39c10 | ||
|
|
979ba59e41 | ||
|
|
db4963048d | ||
|
|
44160cca3f | ||
|
|
2ff6222e9f | ||
|
|
c1e1c144af | ||
|
|
9341e579f7 | ||
|
|
39b505ee95 | ||
|
|
11a678d7fe | ||
|
|
02e2989771 | ||
|
|
1d3720fbcd | ||
|
|
cdebc5b78d | ||
|
|
5c538cca02 | ||
|
|
f58993d041 | ||
|
|
66a4adec66 | ||
|
|
c4728ee83b | ||
|
|
1da3294a8b | ||
|
|
76c4651202 | ||
|
|
5f739d3e26 | ||
|
|
9357e5280c | ||
|
|
8723ae1a33 | ||
|
|
4400e13797 | ||
|
|
d393e00037 | ||
|
|
487f364da7 | ||
|
|
b6f1515be5 | ||
|
|
7a3e380c21 | ||
|
|
5458877f2c | ||
|
|
8e9d0f87f5 | ||
|
|
c6ad65304b | ||
|
|
9857cd372a | ||
|
|
50482c3c07 | ||
|
|
8068131ec3 | ||
|
|
889fffda69 | ||
|
|
197905e841 | ||
|
|
5baab46e60 |
@ -1,11 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.7.0)
|
||||
|
||||
set(UUID_MAIN_PROJECT OFF)
|
||||
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
set(UUID_MAIN_PROJECT ON)
|
||||
endif()
|
||||
|
||||
project(stduuid CXX)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
option(UUID_BUILD_TESTS "Build the unit tests" ON)
|
||||
option(UUID_BUILD_TESTS "Build the unit tests" ${UUID_MAIN_PROJECT})
|
||||
option(UUID_SYSTEM_GENERATOR "Enable operating system uuid generator" OFF)
|
||||
option(UUID_TIME_GENERATOR "Enable experimental time-based uuid generator" OFF)
|
||||
option(UUID_USING_CXX20_SPAN "Using span from std instead of gsl" OFF)
|
||||
option(UUID_ENABLE_INSTALL "Create an install target" ${UUID_MAIN_PROJECT})
|
||||
|
||||
# Library target
|
||||
add_library(${PROJECT_NAME} INTERFACE)
|
||||
@ -38,36 +45,38 @@ endif()
|
||||
# Using span from std
|
||||
if (NOT UUID_USING_CXX20_SPAN)
|
||||
target_include_directories(${PROJECT_NAME} INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/gsl>
|
||||
$<INSTALL_INTERFACE:include/gsl>)
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
install(DIRECTORY gsl DESTINATION include)
|
||||
endif ()
|
||||
|
||||
# Install step and imported target
|
||||
install(FILES include/uuid.h DESTINATION include)
|
||||
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}-targets)
|
||||
install(EXPORT ${PROJECT_NAME}-targets
|
||||
DESTINATION lib/cmake/${PROJECT_NAME})
|
||||
if(UUID_ENABLE_INSTALL)
|
||||
# Install step and imported target
|
||||
install(FILES include/uuid.h DESTINATION include)
|
||||
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}-targets)
|
||||
install(EXPORT ${PROJECT_NAME}-targets
|
||||
DESTINATION lib/cmake/${PROJECT_NAME})
|
||||
|
||||
# Config files for find_package()
|
||||
include(CMakePackageConfigHelpers)
|
||||
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
|
||||
INSTALL_DESTINATION lib/cmake/${PROJECT_NAME})
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-version.cmake"
|
||||
VERSION "1.0"
|
||||
COMPATIBILITY AnyNewerVersion)
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-version.cmake"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindLibuuid.cmake"
|
||||
DESTINATION lib/cmake/${PROJECT_NAME})
|
||||
export(EXPORT ${PROJECT_NAME}-targets
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}-targets.cmake")
|
||||
# Config files for find_package()
|
||||
include(CMakePackageConfigHelpers)
|
||||
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
|
||||
INSTALL_DESTINATION lib/cmake/${PROJECT_NAME})
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-version.cmake"
|
||||
VERSION "1.0"
|
||||
COMPATIBILITY AnyNewerVersion)
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-version.cmake"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindLibuuid.cmake"
|
||||
DESTINATION lib/cmake/${PROJECT_NAME})
|
||||
export(EXPORT ${PROJECT_NAME}-targets
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}-targets.cmake")
|
||||
endif()
|
||||
|
||||
# Tests
|
||||
if (UUID_BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
56
P0959.md
56
P0959.md
@ -105,6 +105,7 @@ P0959R2 was discussed by LEWGI in Kona with the following feedback:
|
||||
Based on this feedback and further considerations, the following changes have been done in this version:
|
||||
* Added detailed explanation of the algorithms for generating uuids.
|
||||
* `from_string()` and non-member `swap()` declared with `noexcept`.
|
||||
* `from_string()` and `is_valid_uuid()` declared with `constexpr`.
|
||||
* The `uuid` type is now defined as a class.
|
||||
* Added an exposition-only member to hint and the possible internal representation of the uuid data.
|
||||
* Added `uuid` constructors from arrays.
|
||||
@ -506,21 +507,11 @@ namespace std {
|
||||
|
||||
constexpr std::strong_ordering operator<=>(uuid const&) const noexcept = default;
|
||||
|
||||
template<class CharT = char>
|
||||
static bool is_valid_uuid(CharT const * str) noexcept;
|
||||
|
||||
template<class CharT = char,
|
||||
class Traits = std::char_traits<CharT>,
|
||||
class Allocator = std::allocator<CharT>>
|
||||
static bool is_valid_uuid(std::basic_string<CharT, Traits, Allocator> const & str) noexcept;
|
||||
|
||||
template<class CharT = char>
|
||||
static uuid from_string(CharT const * str) noexcept;
|
||||
|
||||
template<class CharT = char,
|
||||
class Traits = std::char_traits<CharT>,
|
||||
class Allocator = std::allocator<CharT>>
|
||||
static uuid from_string(std::basic_string<CharT, Traits, Allocator> const & str) noexcept;
|
||||
template<typename StringType>
|
||||
constexpr static bool is_valid_uuid(StringType const & str) noexcept;
|
||||
|
||||
template<typename StringType>
|
||||
constexpr static uuid from_string(StringType const & str) noexcept;
|
||||
private:
|
||||
template <class Elem, class Traits>
|
||||
friend std::basic_ostream<Elem, Traits> & operator<<(std::basic_ostream<Elem, Traits> &s, uuid const & id);
|
||||
@ -598,13 +589,8 @@ namespace std {
|
||||
public:
|
||||
explicit uuid_name_generator(uuid const& namespace_uuid) noexcept;
|
||||
|
||||
template<class CharT = char>
|
||||
uuid operator()(CharT const * name);
|
||||
|
||||
template<class CharT = char,
|
||||
class Traits = std::char_traits<CharT>,
|
||||
class Allocator = std::allocator<CharT>>
|
||||
uuid operator()(std::basic_string<CharT, Traits, Allocator> const & name);
|
||||
template<typename StringType>
|
||||
uuid operator()(StringType const & name);
|
||||
};
|
||||
}
|
||||
```
|
||||
@ -714,21 +700,11 @@ namespace std {
|
||||
|
||||
constexpr std::span<std::byte const, 16> as_bytes() const;
|
||||
|
||||
template<class CharT = char>
|
||||
static bool is_valid_uuid(CharT const * str) noexcept;
|
||||
|
||||
template<class CharT = char,
|
||||
class Traits = std::char_traits<CharT>,
|
||||
class Allocator = std::allocator<CharT>>
|
||||
static bool is_valid_uuid(std::basic_string<CharT, Traits, Allocator> const & str) noexcept;
|
||||
|
||||
template<class CharT = char>
|
||||
static uuid from_string(CharT const * str) noexcept;
|
||||
|
||||
template<class CharT = char,
|
||||
class Traits = std::char_traits<CharT>,
|
||||
class Allocator = std::allocator<CharT>>
|
||||
static uuid from_string(std::basic_string<CharT, Traits, Allocator> const & str) noexcept;
|
||||
template<typename StringType>
|
||||
constexpr static bool is_valid_uuid(StringType const & str) noexcept;
|
||||
|
||||
template<typename StringType>
|
||||
constexpr static uuid from_string(StringType const & str) noexcept;
|
||||
private:
|
||||
template <class Elem, class Traits>
|
||||
friend std::basic_ostream<Elem, Traits> & operator<<(std::basic_ostream<Elem, Traits> &s, uuid const & id);
|
||||
@ -841,6 +817,12 @@ auto id1 = uuid::from_string<char, std::char_traits<char>>(
|
||||
std::string str{ "47183823-2574-4bfd-b411-99ed177d3e43" };
|
||||
auto id2 = uuid::from_string<char, std::char_traits<char>>(str); // [2]
|
||||
```
|
||||
However, to simplify the definition, these two overloads have been replaced with a single one:
|
||||
```
|
||||
template <typename StringType>
|
||||
constexpr static std::optional<uuid> from_string(StringType const & str);
|
||||
```
|
||||
The `is_valid_uuid()` function and the `uuid_name_generator`'s call operator are now defined in the same manner.
|
||||
|
||||
#### Need more explanations about the choices of ordering and how the RFC works in that regard.
|
||||
|
||||
|
||||
39
README.md
39
README.md
@ -27,6 +27,7 @@ Generators:
|
||||
| `uuid_random_generator` | a `basic_uuid_random_generator` using the Mersenne Twister engine (`basic_uuid_random_generator<std::mt19937>`) |
|
||||
| `uuid_name_generator` | a function object that generates version 5, name-based UUIDs using SHA1 hashing. |
|
||||
| `uuid_system_generator` | a function object that generates new UUIDs using operating system resources (`CoCreateGuid` on Windows, `uuid_generate` on Linux, `CFUUIDCreate` on Mac) <br><br> **Note**: This is not part of the standard proposal. It is available only if the `UUID_SYSTEM_GENERATOR` macro is defined. |
|
||||
| `uuid_time_generator` | an experimental function object that generates time-based UUIDs.<br><br> **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. |
|
||||
|
||||
Utilities:
|
||||
|
||||
@ -255,12 +256,28 @@ The following is a list of examples for using the library:
|
||||
auto h2 = std::hash<uuid>{};
|
||||
assert(h1(str) == h2(id));
|
||||
```
|
||||
|
||||
### Random uuids
|
||||
|
||||
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:
|
||||
|
||||
> 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.
|
||||
|
||||
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.
|
||||
|
||||
A portable alternative is to use the [Boost.Random](https://www.boost.org/doc/libs/1_78_0/doc/html/boost_random.html) library.
|
||||
|
||||
## Support
|
||||
The library is supported on all major operating systems: Windows, Linux and Mac OS.
|
||||
|
||||
## Dependencies
|
||||
Because no major compiler supports `std::span` yet the [Microsoft Guidelines Support Library](https://github.com/Microsoft/GSL) (aka GSL) is used for its span implementation (from which the standard version was defined).
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Testing
|
||||
A testing project is available in the sources. To build and execute the tests do the following:
|
||||
@ -270,5 +287,25 @@ A testing project is available in the sources. To build and execute the tests do
|
||||
* Build the project created in the previous step
|
||||
* Run the executable.
|
||||
|
||||
**Examples**
|
||||
|
||||
To generate a project files for Visual Studio 2019, you can run the following commands:
|
||||
```
|
||||
cd build
|
||||
cmake -G "Visual Studio 17" -A x64 ..
|
||||
```
|
||||
|
||||
To enable the operating system uuid generator set the `UUID_SYSTEM_GENERATOR` variable to `ON`.
|
||||
```
|
||||
cd build
|
||||
cmake -G "Visual Studio 17" -A x64 -DUUID_SYSTEM_GENERATOR=ON ..
|
||||
```
|
||||
|
||||
To enable the experimental time-based uuid generator set the `UUID_TIME_GENERATOR` variable to `ON`.
|
||||
```
|
||||
cd build
|
||||
cmake -G "Visual Studio 17" -A x64 -DUUID_TIME_GENERATOR=ON ..
|
||||
```
|
||||
|
||||
## Credits
|
||||
The SHA1 implementation is based on the [TinySHA1](https://github.com/mohaps/TinySHA1) library.
|
||||
|
||||
17
appveyor.yml
17
appveyor.yml
@ -2,12 +2,21 @@ version: '{build}'
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
CMAKE_GENERATOR: Visual Studio 15 2017
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
CMAKE_GENERATOR: Visual Studio 16 2019
|
||||
CMAKE_GENERATOR_PLATFORM: Win32
|
||||
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
CMAKE_GENERATOR: Visual Studio 15 2017
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
CMAKE_GENERATOR: Visual Studio 16 2019
|
||||
CMAKE_GENERATOR_PLATFORM: x64
|
||||
CMAKE_CLI_FLAGS: -DUUID_SYSTEM_GENERATOR=ON -DUUID_TIME_GENERATOR=ON
|
||||
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
CMAKE_GENERATOR: Visual Studio 17 2022
|
||||
CMAKE_GENERATOR_PLATFORM: Win32
|
||||
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
CMAKE_GENERATOR: Visual Studio 17 2022
|
||||
CMAKE_GENERATOR_PLATFORM: x64
|
||||
CMAKE_CLI_FLAGS: -DUUID_SYSTEM_GENERATOR=ON -DUUID_TIME_GENERATOR=ON
|
||||
|
||||
|
||||
470
catch/catch.hpp
470
catch/catch.hpp
@ -1,9 +1,9 @@
|
||||
/*
|
||||
* Catch v2.13.3
|
||||
* Generated: 2020-10-31 18:20:31.045274
|
||||
* Catch v2.13.7
|
||||
* Generated: 2021-07-28 20:29:27.753164
|
||||
* ----------------------------------------------------------
|
||||
* This file has been merged from multiple headers. Please don't edit it directly
|
||||
* Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved.
|
||||
* Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@ -15,7 +15,7 @@
|
||||
|
||||
#define CATCH_VERSION_MAJOR 2
|
||||
#define CATCH_VERSION_MINOR 13
|
||||
#define CATCH_VERSION_PATCH 3
|
||||
#define CATCH_VERSION_PATCH 7
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang system_header
|
||||
@ -66,13 +66,16 @@
|
||||
#if !defined(CATCH_CONFIG_IMPL_ONLY)
|
||||
// start catch_platform.h
|
||||
|
||||
// See e.g.:
|
||||
// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
|
||||
#ifdef __APPLE__
|
||||
# include <TargetConditionals.h>
|
||||
# if TARGET_OS_OSX == 1
|
||||
# define CATCH_PLATFORM_MAC
|
||||
# elif TARGET_OS_IPHONE == 1
|
||||
# define CATCH_PLATFORM_IPHONE
|
||||
# endif
|
||||
# include <TargetConditionals.h>
|
||||
# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
|
||||
(defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
|
||||
# define CATCH_PLATFORM_MAC
|
||||
# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
|
||||
# define CATCH_PLATFORM_IPHONE
|
||||
# endif
|
||||
|
||||
#elif defined(linux) || defined(__linux) || defined(__linux__)
|
||||
# define CATCH_PLATFORM_LINUX
|
||||
@ -132,9 +135,9 @@ namespace Catch {
|
||||
|
||||
#endif
|
||||
|
||||
// We have to avoid both ICC and Clang, because they try to mask themselves
|
||||
// as gcc, and we want only GCC in this block
|
||||
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__)
|
||||
// Only GCC compiler should be used in this block, so other compilers trying to
|
||||
// mask themselves as GCC should be ignored.
|
||||
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
|
||||
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
|
||||
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
|
||||
|
||||
@ -323,7 +326,7 @@ namespace Catch {
|
||||
// Check if byte is available and usable
|
||||
# if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
# include <cstddef>
|
||||
# if __cpp_lib_byte > 0
|
||||
# if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0)
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_BYTE
|
||||
# endif
|
||||
# endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
@ -5455,6 +5458,8 @@ namespace Catch {
|
||||
} // namespace Catch
|
||||
|
||||
// end catch_outlier_classification.hpp
|
||||
|
||||
#include <iterator>
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
#include <string>
|
||||
@ -6339,9 +6344,10 @@ namespace Catch {
|
||||
|
||||
void writeTestCase(TestCaseNode const& testCaseNode);
|
||||
|
||||
void writeSection(std::string const& className,
|
||||
std::string const& rootName,
|
||||
SectionNode const& sectionNode);
|
||||
void writeSection( std::string const& className,
|
||||
std::string const& rootName,
|
||||
SectionNode const& sectionNode,
|
||||
bool testOkToFail );
|
||||
|
||||
void writeAssertions(SectionNode const& sectionNode);
|
||||
void writeAssertion(AssertionStats const& stats);
|
||||
@ -6876,7 +6882,7 @@ namespace Catch {
|
||||
}
|
||||
iters *= 2;
|
||||
}
|
||||
throw optimized_away_error{};
|
||||
Catch::throw_exception(optimized_away_error{});
|
||||
}
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
@ -6884,6 +6890,7 @@ namespace Catch {
|
||||
|
||||
// end catch_run_for_at_least.hpp
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
@ -7054,8 +7061,8 @@ namespace Catch {
|
||||
double b2 = bias - z1;
|
||||
double a1 = a(b1);
|
||||
double a2 = a(b2);
|
||||
auto lo = std::max(cumn(a1), 0);
|
||||
auto hi = std::min(cumn(a2), n - 1);
|
||||
auto lo = (std::max)(cumn(a1), 0);
|
||||
auto hi = (std::min)(cumn(a2), n - 1);
|
||||
|
||||
return { point, resample[lo], resample[hi], confidence_level };
|
||||
}
|
||||
@ -7124,7 +7131,9 @@ namespace Catch {
|
||||
}
|
||||
template <typename Clock>
|
||||
EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) {
|
||||
auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, FloatDuration<Clock>(clock_cost_estimation_time_limit));
|
||||
auto time_limit = (std::min)(
|
||||
resolution * clock_cost_estimation_tick_limit,
|
||||
FloatDuration<Clock>(clock_cost_estimation_time_limit));
|
||||
auto time_clock = [](int k) {
|
||||
return Detail::measure<Clock>([k] {
|
||||
for (int i = 0; i < k; ++i) {
|
||||
@ -7771,7 +7780,7 @@ namespace Catch {
|
||||
double sb = stddev.point;
|
||||
double mn = mean.point / n;
|
||||
double mg_min = mn / 2.;
|
||||
double sg = std::min(mg_min / 4., sb / std::sqrt(n));
|
||||
double sg = (std::min)(mg_min / 4., sb / std::sqrt(n));
|
||||
double sg2 = sg * sg;
|
||||
double sb2 = sb * sb;
|
||||
|
||||
@ -7790,7 +7799,7 @@ namespace Catch {
|
||||
return (nc / n) * (sb2 - nc * sg2);
|
||||
};
|
||||
|
||||
return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / sb2;
|
||||
return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2;
|
||||
}
|
||||
|
||||
bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
|
||||
@ -7980,86 +7989,58 @@ namespace Catch {
|
||||
|
||||
// start catch_fatal_condition.h
|
||||
|
||||
// start catch_windows_h_proxy.h
|
||||
|
||||
|
||||
#if defined(CATCH_PLATFORM_WINDOWS)
|
||||
|
||||
#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
|
||||
# define CATCH_DEFINED_NOMINMAX
|
||||
# define NOMINMAX
|
||||
#endif
|
||||
#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
|
||||
# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#ifdef __AFXDLL
|
||||
#include <AfxWin.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef CATCH_DEFINED_NOMINMAX
|
||||
# undef NOMINMAX
|
||||
#endif
|
||||
#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
|
||||
# undef WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#endif // defined(CATCH_PLATFORM_WINDOWS)
|
||||
|
||||
// end catch_windows_h_proxy.h
|
||||
#if defined( CATCH_CONFIG_WINDOWS_SEH )
|
||||
#include <cassert>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct FatalConditionHandler {
|
||||
|
||||
static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo);
|
||||
FatalConditionHandler();
|
||||
static void reset();
|
||||
~FatalConditionHandler();
|
||||
|
||||
private:
|
||||
static bool isSet;
|
||||
static ULONG guaranteeSize;
|
||||
static PVOID exceptionHandlerHandle;
|
||||
};
|
||||
|
||||
} // namespace Catch
|
||||
|
||||
#elif defined ( CATCH_CONFIG_POSIX_SIGNALS )
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct FatalConditionHandler {
|
||||
|
||||
static bool isSet;
|
||||
static struct sigaction oldSigActions[];
|
||||
static stack_t oldSigStack;
|
||||
static char altStackMem[];
|
||||
|
||||
static void handleSignal( int sig );
|
||||
// Wrapper for platform-specific fatal error (signals/SEH) handlers
|
||||
//
|
||||
// Tries to be cooperative with other handlers, and not step over
|
||||
// other handlers. This means that unknown structured exceptions
|
||||
// are passed on, previous signal handlers are called, and so on.
|
||||
//
|
||||
// Can only be instantiated once, and assumes that once a signal
|
||||
// is caught, the binary will end up terminating. Thus, there
|
||||
class FatalConditionHandler {
|
||||
bool m_started = false;
|
||||
|
||||
// Install/disengage implementation for specific platform.
|
||||
// Should be if-defed to work on current platform, can assume
|
||||
// engage-disengage 1:1 pairing.
|
||||
void engage_platform();
|
||||
void disengage_platform();
|
||||
public:
|
||||
// Should also have platform-specific implementations as needed
|
||||
FatalConditionHandler();
|
||||
~FatalConditionHandler();
|
||||
static void reset();
|
||||
|
||||
void engage() {
|
||||
assert(!m_started && "Handler cannot be installed twice.");
|
||||
m_started = true;
|
||||
engage_platform();
|
||||
}
|
||||
|
||||
void disengage() {
|
||||
assert(m_started && "Handler cannot be uninstalled without being installed first");
|
||||
m_started = false;
|
||||
disengage_platform();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Catch
|
||||
|
||||
#else
|
||||
|
||||
namespace Catch {
|
||||
struct FatalConditionHandler {
|
||||
void reset();
|
||||
//! Simple RAII guard for (dis)engaging the FatalConditionHandler
|
||||
class FatalConditionHandlerGuard {
|
||||
FatalConditionHandler* m_handler;
|
||||
public:
|
||||
FatalConditionHandlerGuard(FatalConditionHandler* handler):
|
||||
m_handler(handler) {
|
||||
m_handler->engage();
|
||||
}
|
||||
~FatalConditionHandlerGuard() {
|
||||
m_handler->disengage();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
} // end namespace Catch
|
||||
|
||||
// end catch_fatal_condition.h
|
||||
#include <string>
|
||||
@ -8185,6 +8166,7 @@ namespace Catch {
|
||||
std::vector<SectionEndInfo> m_unfinishedSections;
|
||||
std::vector<ITracker*> m_activeSections;
|
||||
TrackerContext m_trackerContext;
|
||||
FatalConditionHandler m_fatalConditionhandler;
|
||||
bool m_lastAssertionPassed = false;
|
||||
bool m_shouldReportUnexpected = true;
|
||||
bool m_includeSuccessfulResults;
|
||||
@ -10057,6 +10039,36 @@ namespace Catch {
|
||||
}
|
||||
|
||||
// end catch_errno_guard.h
|
||||
// start catch_windows_h_proxy.h
|
||||
|
||||
|
||||
#if defined(CATCH_PLATFORM_WINDOWS)
|
||||
|
||||
#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
|
||||
# define CATCH_DEFINED_NOMINMAX
|
||||
# define NOMINMAX
|
||||
#endif
|
||||
#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
|
||||
# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#ifdef __AFXDLL
|
||||
#include <AfxWin.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef CATCH_DEFINED_NOMINMAX
|
||||
# undef NOMINMAX
|
||||
#endif
|
||||
#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
|
||||
# undef WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#endif // defined(CATCH_PLATFORM_WINDOWS)
|
||||
|
||||
// end catch_windows_h_proxy.h
|
||||
#include <sstream>
|
||||
|
||||
namespace Catch {
|
||||
@ -10573,7 +10585,7 @@ namespace Catch {
|
||||
// Extracts the actual name part of an enum instance
|
||||
// In other words, it returns the Blue part of Bikeshed::Colour::Blue
|
||||
StringRef extractInstanceName(StringRef enumInstance) {
|
||||
// Find last occurence of ":"
|
||||
// Find last occurrence of ":"
|
||||
size_t name_start = enumInstance.size();
|
||||
while (name_start > 0 && enumInstance[name_start - 1] != ':') {
|
||||
--name_start;
|
||||
@ -10735,25 +10747,47 @@ namespace Catch {
|
||||
// end catch_exception_translator_registry.cpp
|
||||
// start catch_fatal_condition.cpp
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#endif
|
||||
#include <algorithm>
|
||||
|
||||
#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS )
|
||||
|
||||
namespace Catch {
|
||||
|
||||
// If neither SEH nor signal handling is required, the handler impls
|
||||
// do not have to do anything, and can be empty.
|
||||
void FatalConditionHandler::engage_platform() {}
|
||||
void FatalConditionHandler::disengage_platform() {}
|
||||
FatalConditionHandler::FatalConditionHandler() = default;
|
||||
FatalConditionHandler::~FatalConditionHandler() = default;
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS
|
||||
|
||||
#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS )
|
||||
#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time"
|
||||
#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS
|
||||
|
||||
#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
|
||||
|
||||
namespace {
|
||||
// Report the error condition
|
||||
//! Signals fatal error message to the run context
|
||||
void reportFatal( char const * const message ) {
|
||||
Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message );
|
||||
}
|
||||
}
|
||||
|
||||
#endif // signals/SEH handling
|
||||
//! Minimal size Catch2 needs for its own fatal error handling.
|
||||
//! Picked anecdotally, so it might not be sufficient on all
|
||||
//! platforms, and for all configurations.
|
||||
constexpr std::size_t minStackSizeForErrors = 32 * 1024;
|
||||
} // end unnamed namespace
|
||||
|
||||
#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS
|
||||
|
||||
#if defined( CATCH_CONFIG_WINDOWS_SEH )
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct SignalDefs { DWORD id; const char* name; };
|
||||
|
||||
// There is no 1-1 mapping between signals and windows exceptions.
|
||||
@ -10766,7 +10800,7 @@ namespace Catch {
|
||||
{ static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" },
|
||||
};
|
||||
|
||||
LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
|
||||
static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
|
||||
for (auto const& def : signalDefs) {
|
||||
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
|
||||
reportFatal(def.name);
|
||||
@ -10777,38 +10811,50 @@ namespace Catch {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
FatalConditionHandler::FatalConditionHandler() {
|
||||
isSet = true;
|
||||
// 32k seems enough for Catch to handle stack overflow,
|
||||
// but the value was found experimentally, so there is no strong guarantee
|
||||
guaranteeSize = 32 * 1024;
|
||||
exceptionHandlerHandle = nullptr;
|
||||
// Register as first handler in current chain
|
||||
exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
|
||||
// Pass in guarantee size to be filled
|
||||
SetThreadStackGuarantee(&guaranteeSize);
|
||||
}
|
||||
// Since we do not support multiple instantiations, we put these
|
||||
// into global variables and rely on cleaning them up in outlined
|
||||
// constructors/destructors
|
||||
static PVOID exceptionHandlerHandle = nullptr;
|
||||
|
||||
void FatalConditionHandler::reset() {
|
||||
if (isSet) {
|
||||
RemoveVectoredExceptionHandler(exceptionHandlerHandle);
|
||||
SetThreadStackGuarantee(&guaranteeSize);
|
||||
exceptionHandlerHandle = nullptr;
|
||||
isSet = false;
|
||||
// For MSVC, we reserve part of the stack memory for handling
|
||||
// memory overflow structured exception.
|
||||
FatalConditionHandler::FatalConditionHandler() {
|
||||
ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors);
|
||||
if (!SetThreadStackGuarantee(&guaranteeSize)) {
|
||||
// We do not want to fully error out, because needing
|
||||
// the stack reserve should be rare enough anyway.
|
||||
Catch::cerr()
|
||||
<< "Failed to reserve piece of stack."
|
||||
<< " Stack overflows will not be reported successfully.";
|
||||
}
|
||||
}
|
||||
|
||||
FatalConditionHandler::~FatalConditionHandler() {
|
||||
reset();
|
||||
// We do not attempt to unset the stack guarantee, because
|
||||
// Windows does not support lowering the stack size guarantee.
|
||||
FatalConditionHandler::~FatalConditionHandler() = default;
|
||||
|
||||
void FatalConditionHandler::engage_platform() {
|
||||
// Register as first handler in current chain
|
||||
exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
|
||||
if (!exceptionHandlerHandle) {
|
||||
CATCH_RUNTIME_ERROR("Could not register vectored exception handler");
|
||||
}
|
||||
}
|
||||
|
||||
bool FatalConditionHandler::isSet = false;
|
||||
ULONG FatalConditionHandler::guaranteeSize = 0;
|
||||
PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
|
||||
void FatalConditionHandler::disengage_platform() {
|
||||
if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
|
||||
CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler");
|
||||
}
|
||||
exceptionHandlerHandle = nullptr;
|
||||
}
|
||||
|
||||
} // namespace Catch
|
||||
} // end namespace Catch
|
||||
|
||||
#elif defined( CATCH_CONFIG_POSIX_SIGNALS )
|
||||
#endif // CATCH_CONFIG_WINDOWS_SEH
|
||||
|
||||
#if defined( CATCH_CONFIG_POSIX_SIGNALS )
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@ -10817,10 +10863,6 @@ namespace Catch {
|
||||
const char* name;
|
||||
};
|
||||
|
||||
// 32kb for the alternate stack seems to be sufficient. However, this value
|
||||
// is experimentally determined, so that's not guaranteed.
|
||||
static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
|
||||
|
||||
static SignalDefs signalDefs[] = {
|
||||
{ SIGINT, "SIGINT - Terminal interrupt signal" },
|
||||
{ SIGILL, "SIGILL - Illegal instruction signal" },
|
||||
@ -10830,7 +10872,32 @@ namespace Catch {
|
||||
{ SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
|
||||
};
|
||||
|
||||
void FatalConditionHandler::handleSignal( int sig ) {
|
||||
// Older GCCs trigger -Wmissing-field-initializers for T foo = {}
|
||||
// which is zero initialization, but not explicit. We want to avoid
|
||||
// that.
|
||||
#if defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#endif
|
||||
|
||||
static char* altStackMem = nullptr;
|
||||
static std::size_t altStackSize = 0;
|
||||
static stack_t oldSigStack{};
|
||||
static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{};
|
||||
|
||||
static void restorePreviousSignalHandlers() {
|
||||
// We set signal handlers back to the previous ones. Hopefully
|
||||
// nobody overwrote them in the meantime, and doesn't expect
|
||||
// their signal handlers to live past ours given that they
|
||||
// installed them after ours..
|
||||
for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
|
||||
sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
|
||||
}
|
||||
// Return the old stack
|
||||
sigaltstack(&oldSigStack, nullptr);
|
||||
}
|
||||
|
||||
static void handleSignal( int sig ) {
|
||||
char const * name = "<unknown signal>";
|
||||
for (auto const& def : signalDefs) {
|
||||
if (sig == def.id) {
|
||||
@ -10838,16 +10905,33 @@ namespace Catch {
|
||||
break;
|
||||
}
|
||||
}
|
||||
reset();
|
||||
reportFatal(name);
|
||||
// We need to restore previous signal handlers and let them do
|
||||
// their thing, so that the users can have the debugger break
|
||||
// when a signal is raised, and so on.
|
||||
restorePreviousSignalHandlers();
|
||||
reportFatal( name );
|
||||
raise( sig );
|
||||
}
|
||||
|
||||
FatalConditionHandler::FatalConditionHandler() {
|
||||
isSet = true;
|
||||
assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists");
|
||||
if (altStackSize == 0) {
|
||||
altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors);
|
||||
}
|
||||
altStackMem = new char[altStackSize]();
|
||||
}
|
||||
|
||||
FatalConditionHandler::~FatalConditionHandler() {
|
||||
delete[] altStackMem;
|
||||
// We signal that another instance can be constructed by zeroing
|
||||
// out the pointer.
|
||||
altStackMem = nullptr;
|
||||
}
|
||||
|
||||
void FatalConditionHandler::engage_platform() {
|
||||
stack_t sigStack;
|
||||
sigStack.ss_sp = altStackMem;
|
||||
sigStack.ss_size = sigStackSize;
|
||||
sigStack.ss_size = altStackSize;
|
||||
sigStack.ss_flags = 0;
|
||||
sigaltstack(&sigStack, &oldSigStack);
|
||||
struct sigaction sa = { };
|
||||
@ -10859,40 +10943,17 @@ namespace Catch {
|
||||
}
|
||||
}
|
||||
|
||||
FatalConditionHandler::~FatalConditionHandler() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void FatalConditionHandler::reset() {
|
||||
if( isSet ) {
|
||||
// Set signals back to previous values -- hopefully nobody overwrote them in the meantime
|
||||
for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
|
||||
sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
|
||||
}
|
||||
// Return the old stack
|
||||
sigaltstack(&oldSigStack, nullptr);
|
||||
isSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FatalConditionHandler::isSet = false;
|
||||
struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
|
||||
stack_t FatalConditionHandler::oldSigStack = {};
|
||||
char FatalConditionHandler::altStackMem[sigStackSize] = {};
|
||||
|
||||
} // namespace Catch
|
||||
|
||||
#else
|
||||
|
||||
namespace Catch {
|
||||
void FatalConditionHandler::reset() {}
|
||||
}
|
||||
|
||||
#endif // signals/SEH handling
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
void FatalConditionHandler::disengage_platform() {
|
||||
restorePreviousSignalHandlers();
|
||||
}
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // CATCH_CONFIG_POSIX_SIGNALS
|
||||
// end catch_fatal_condition.cpp
|
||||
// start catch_generators.cpp
|
||||
|
||||
@ -11447,7 +11508,8 @@ namespace {
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
auto ulpDiff = std::abs(lc - rc);
|
||||
// static cast as a workaround for IBM XLC
|
||||
auto ulpDiff = std::abs(static_cast<FP>(lc - rc));
|
||||
return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
|
||||
}
|
||||
|
||||
@ -11621,7 +11683,6 @@ Floating::WithinRelMatcher WithinRel(float target) {
|
||||
|
||||
} // namespace Matchers
|
||||
} // namespace Catch
|
||||
|
||||
// end catch_matchers_floating.cpp
|
||||
// start catch_matchers_generic.cpp
|
||||
|
||||
@ -12955,9 +13016,8 @@ namespace Catch {
|
||||
}
|
||||
|
||||
void RunContext::invokeActiveTestCase() {
|
||||
FatalConditionHandler fatalConditionHandler; // Handle signals
|
||||
FatalConditionHandlerGuard _(&m_fatalConditionhandler);
|
||||
m_activeTestCase->invoke();
|
||||
fatalConditionHandler.reset();
|
||||
}
|
||||
|
||||
void RunContext::handleUnfinishedSections() {
|
||||
@ -14126,24 +14186,28 @@ namespace Catch {
|
||||
|
||||
namespace {
|
||||
struct TestHasher {
|
||||
explicit TestHasher(Catch::SimplePcg32& rng_instance) {
|
||||
basis = rng_instance();
|
||||
basis <<= 32;
|
||||
basis |= rng_instance();
|
||||
}
|
||||
using hash_t = uint64_t;
|
||||
|
||||
uint64_t basis;
|
||||
explicit TestHasher( hash_t hashSuffix ):
|
||||
m_hashSuffix{ hashSuffix } {}
|
||||
|
||||
uint64_t operator()(TestCase const& t) const {
|
||||
// Modified FNV-1a hash
|
||||
static constexpr uint64_t prime = 1099511628211;
|
||||
uint64_t hash = basis;
|
||||
for (const char c : t.name) {
|
||||
uint32_t operator()( TestCase const& t ) const {
|
||||
// FNV-1a hash with multiplication fold.
|
||||
const hash_t prime = 1099511628211u;
|
||||
hash_t hash = 14695981039346656037u;
|
||||
for ( const char c : t.name ) {
|
||||
hash ^= c;
|
||||
hash *= prime;
|
||||
}
|
||||
return hash;
|
||||
hash ^= m_hashSuffix;
|
||||
hash *= prime;
|
||||
const uint32_t low{ static_cast<uint32_t>( hash ) };
|
||||
const uint32_t high{ static_cast<uint32_t>( hash >> 32 ) };
|
||||
return low * high;
|
||||
}
|
||||
|
||||
private:
|
||||
hash_t m_hashSuffix;
|
||||
};
|
||||
} // end unnamed namespace
|
||||
|
||||
@ -14161,9 +14225,9 @@ namespace Catch {
|
||||
|
||||
case RunTests::InRandomOrder: {
|
||||
seedRng( config );
|
||||
TestHasher h( rng() );
|
||||
TestHasher h{ config.rngSeed() };
|
||||
|
||||
using hashedTest = std::pair<uint64_t, TestCase const*>;
|
||||
using hashedTest = std::pair<TestHasher::hash_t, TestCase const*>;
|
||||
std::vector<hashedTest> indexed_tests;
|
||||
indexed_tests.reserve( unsortedTestCases.size() );
|
||||
|
||||
@ -15316,7 +15380,7 @@ namespace Catch {
|
||||
}
|
||||
|
||||
Version const& libraryVersion() {
|
||||
static Version version( 2, 13, 3, "", 0 );
|
||||
static Version version( 2, 13, 7, "", 0 );
|
||||
return version;
|
||||
}
|
||||
|
||||
@ -16729,6 +16793,7 @@ CATCH_REGISTER_REPORTER("console", ConsoleReporter)
|
||||
#include <sstream>
|
||||
#include <ctime>
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@ -16756,7 +16821,7 @@ namespace Catch {
|
||||
#else
|
||||
std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
|
||||
#endif
|
||||
return std::string(timeStamp);
|
||||
return std::string(timeStamp, timeStampSize-1);
|
||||
}
|
||||
|
||||
std::string fileNameTag(const std::vector<std::string> &tags) {
|
||||
@ -16767,6 +16832,17 @@ namespace Catch {
|
||||
return it->substr(1);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// Formats the duration in seconds to 3 decimal places.
|
||||
// This is done because some genius defined Maven Surefire schema
|
||||
// in a way that only accepts 3 decimal places, and tools like
|
||||
// Jenkins use that schema for validation JUnit reporter output.
|
||||
std::string formatDuration( double seconds ) {
|
||||
ReusableStringStream rss;
|
||||
rss << std::fixed << std::setprecision( 3 ) << seconds;
|
||||
return rss.str();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
JunitReporter::JunitReporter( ReporterConfig const& _config )
|
||||
@ -16836,7 +16912,7 @@ namespace Catch {
|
||||
if( m_config->showDurations() == ShowDurations::Never )
|
||||
xml.writeAttribute( "time", "" );
|
||||
else
|
||||
xml.writeAttribute( "time", suiteTime );
|
||||
xml.writeAttribute( "time", formatDuration( suiteTime ) );
|
||||
xml.writeAttribute( "timestamp", getCurrentTimestamp() );
|
||||
|
||||
// Write properties if there are any
|
||||
@ -16881,12 +16957,13 @@ namespace Catch {
|
||||
if ( !m_config->name().empty() )
|
||||
className = m_config->name() + "." + className;
|
||||
|
||||
writeSection( className, "", rootSection );
|
||||
writeSection( className, "", rootSection, stats.testInfo.okToFail() );
|
||||
}
|
||||
|
||||
void JunitReporter::writeSection( std::string const& className,
|
||||
std::string const& rootName,
|
||||
SectionNode const& sectionNode ) {
|
||||
void JunitReporter::writeSection( std::string const& className,
|
||||
std::string const& rootName,
|
||||
SectionNode const& sectionNode,
|
||||
bool testOkToFail) {
|
||||
std::string name = trim( sectionNode.stats.sectionInfo.name );
|
||||
if( !rootName.empty() )
|
||||
name = rootName + '/' + name;
|
||||
@ -16903,13 +16980,18 @@ namespace Catch {
|
||||
xml.writeAttribute( "classname", className );
|
||||
xml.writeAttribute( "name", name );
|
||||
}
|
||||
xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) );
|
||||
xml.writeAttribute( "time", formatDuration( sectionNode.stats.durationInSeconds ) );
|
||||
// This is not ideal, but it should be enough to mimic gtest's
|
||||
// junit output.
|
||||
// Ideally the JUnit reporter would also handle `skipTest`
|
||||
// events and write those out appropriately.
|
||||
xml.writeAttribute( "status", "run" );
|
||||
|
||||
if (sectionNode.stats.assertions.failedButOk) {
|
||||
xml.scopedElement("skipped")
|
||||
.writeAttribute("message", "TEST_CASE tagged with !mayfail");
|
||||
}
|
||||
|
||||
writeAssertions( sectionNode );
|
||||
|
||||
if( !sectionNode.stdOut.empty() )
|
||||
@ -16919,9 +17001,9 @@ namespace Catch {
|
||||
}
|
||||
for( auto const& childNode : sectionNode.childSections )
|
||||
if( className.empty() )
|
||||
writeSection( name, "", *childNode );
|
||||
writeSection( name, "", *childNode, testOkToFail );
|
||||
else
|
||||
writeSection( className, name, *childNode );
|
||||
writeSection( className, name, *childNode, testOkToFail );
|
||||
}
|
||||
|
||||
void JunitReporter::writeAssertions( SectionNode const& sectionNode ) {
|
||||
|
||||
181
catch/catch_reporter_sonarqube.hpp
Normal file
181
catch/catch_reporter_sonarqube.hpp
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Created by Daniel Garcia on 2018-12-04.
|
||||
* Copyright Social Point SL. All rights reserved.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
#ifndef CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
|
||||
#define CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
|
||||
|
||||
|
||||
// Don't #include any Catch headers here - we can assume they are already
|
||||
// included before this header.
|
||||
// This is not good practice in general but is necessary in this case so this
|
||||
// file can be distributed as a single header that works with the main
|
||||
// Catch single header.
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct SonarQubeReporter : CumulativeReporterBase<SonarQubeReporter> {
|
||||
|
||||
SonarQubeReporter(ReporterConfig const& config)
|
||||
: CumulativeReporterBase(config)
|
||||
, xml(config.stream()) {
|
||||
m_reporterPrefs.shouldRedirectStdOut = true;
|
||||
m_reporterPrefs.shouldReportAllAssertions = true;
|
||||
}
|
||||
|
||||
~SonarQubeReporter() override;
|
||||
|
||||
static std::string getDescription() {
|
||||
return "Reports test results in the Generic Test Data SonarQube XML format";
|
||||
}
|
||||
|
||||
static std::set<Verbosity> getSupportedVerbosities() {
|
||||
return { Verbosity::Normal };
|
||||
}
|
||||
|
||||
void noMatchingTestCases(std::string const& /*spec*/) override {}
|
||||
|
||||
void testRunStarting(TestRunInfo const& testRunInfo) override {
|
||||
CumulativeReporterBase::testRunStarting(testRunInfo);
|
||||
xml.startElement("testExecutions");
|
||||
xml.writeAttribute("version", "1");
|
||||
}
|
||||
|
||||
void testGroupEnded(TestGroupStats const& testGroupStats) override {
|
||||
CumulativeReporterBase::testGroupEnded(testGroupStats);
|
||||
writeGroup(*m_testGroups.back());
|
||||
}
|
||||
|
||||
void testRunEndedCumulative() override {
|
||||
xml.endElement();
|
||||
}
|
||||
|
||||
void writeGroup(TestGroupNode const& groupNode) {
|
||||
std::map<std::string, TestGroupNode::ChildNodes> testsPerFile;
|
||||
for(auto const& child : groupNode.children)
|
||||
testsPerFile[child->value.testInfo.lineInfo.file].push_back(child);
|
||||
|
||||
for(auto const& kv : testsPerFile)
|
||||
writeTestFile(kv.first.c_str(), kv.second);
|
||||
}
|
||||
|
||||
void writeTestFile(const char* filename, TestGroupNode::ChildNodes const& testCaseNodes) {
|
||||
XmlWriter::ScopedElement e = xml.scopedElement("file");
|
||||
xml.writeAttribute("path", filename);
|
||||
|
||||
for(auto const& child : testCaseNodes)
|
||||
writeTestCase(*child);
|
||||
}
|
||||
|
||||
void writeTestCase(TestCaseNode const& testCaseNode) {
|
||||
// All test cases have exactly one section - which represents the
|
||||
// test case itself. That section may have 0-n nested sections
|
||||
assert(testCaseNode.children.size() == 1);
|
||||
SectionNode const& rootSection = *testCaseNode.children.front();
|
||||
writeSection("", rootSection, testCaseNode.value.testInfo.okToFail());
|
||||
}
|
||||
|
||||
void writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail) {
|
||||
std::string name = trim(sectionNode.stats.sectionInfo.name);
|
||||
if(!rootName.empty())
|
||||
name = rootName + '/' + name;
|
||||
|
||||
if(!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) {
|
||||
XmlWriter::ScopedElement e = xml.scopedElement("testCase");
|
||||
xml.writeAttribute("name", name);
|
||||
xml.writeAttribute("duration", static_cast<long>(sectionNode.stats.durationInSeconds * 1000));
|
||||
|
||||
writeAssertions(sectionNode, okToFail);
|
||||
}
|
||||
|
||||
for(auto const& childNode : sectionNode.childSections)
|
||||
writeSection(name, *childNode, okToFail);
|
||||
}
|
||||
|
||||
void writeAssertions(SectionNode const& sectionNode, bool okToFail) {
|
||||
for(auto const& assertion : sectionNode.assertions)
|
||||
writeAssertion( assertion, okToFail);
|
||||
}
|
||||
|
||||
void writeAssertion(AssertionStats const& stats, bool okToFail) {
|
||||
AssertionResult const& result = stats.assertionResult;
|
||||
if(!result.isOk()) {
|
||||
std::string elementName;
|
||||
if(okToFail) {
|
||||
elementName = "skipped";
|
||||
}
|
||||
else {
|
||||
switch(result.getResultType()) {
|
||||
case ResultWas::ThrewException:
|
||||
case ResultWas::FatalErrorCondition:
|
||||
elementName = "error";
|
||||
break;
|
||||
case ResultWas::ExplicitFailure:
|
||||
elementName = "failure";
|
||||
break;
|
||||
case ResultWas::ExpressionFailed:
|
||||
elementName = "failure";
|
||||
break;
|
||||
case ResultWas::DidntThrowException:
|
||||
elementName = "failure";
|
||||
break;
|
||||
|
||||
// We should never see these here:
|
||||
case ResultWas::Info:
|
||||
case ResultWas::Warning:
|
||||
case ResultWas::Ok:
|
||||
case ResultWas::Unknown:
|
||||
case ResultWas::FailureBit:
|
||||
case ResultWas::Exception:
|
||||
elementName = "internalError";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
XmlWriter::ScopedElement e = xml.scopedElement(elementName);
|
||||
|
||||
ReusableStringStream messageRss;
|
||||
messageRss << result.getTestMacroName() << "(" << result.getExpression() << ")";
|
||||
xml.writeAttribute("message", messageRss.str());
|
||||
|
||||
ReusableStringStream textRss;
|
||||
if (stats.totals.assertions.total() > 0) {
|
||||
textRss << "FAILED:\n";
|
||||
if (result.hasExpression()) {
|
||||
textRss << "\t" << result.getExpressionInMacro() << "\n";
|
||||
}
|
||||
if (result.hasExpandedExpression()) {
|
||||
textRss << "with expansion:\n\t" << result.getExpandedExpression() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if(!result.getMessage().empty())
|
||||
textRss << result.getMessage() << "\n";
|
||||
|
||||
for(auto const& msg : stats.infoMessages)
|
||||
if(msg.type == ResultWas::Info)
|
||||
textRss << msg.message << "\n";
|
||||
|
||||
textRss << "at " << result.getSourceInfo();
|
||||
xml.writeText(textRss.str(), XmlFormatting::Newline);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
XmlWriter xml;
|
||||
};
|
||||
|
||||
#ifdef CATCH_IMPL
|
||||
SonarQubeReporter::~SonarQubeReporter() {}
|
||||
#endif
|
||||
|
||||
CATCH_REGISTER_REPORTER( "sonarqube", SonarQubeReporter )
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
|
||||
@ -23,18 +23,17 @@ namespace Catch {
|
||||
|
||||
using StreamingReporterBase::StreamingReporterBase;
|
||||
|
||||
TAPReporter( ReporterConfig const& config ):
|
||||
StreamingReporterBase( config ) {
|
||||
m_reporterPrefs.shouldReportAllAssertions = true;
|
||||
}
|
||||
|
||||
~TAPReporter() override;
|
||||
|
||||
static std::string getDescription() {
|
||||
return "Reports test results in TAP format, suitable for test harnesses";
|
||||
}
|
||||
|
||||
ReporterPreferences getPreferences() const override {
|
||||
ReporterPreferences prefs;
|
||||
prefs.shouldRedirectStdOut = false;
|
||||
return prefs;
|
||||
}
|
||||
|
||||
void noMatchingTestCases( std::string const& spec ) override {
|
||||
stream << "# No test cases matched '" << spec << "'" << std::endl;
|
||||
}
|
||||
@ -44,9 +43,9 @@ namespace Catch {
|
||||
bool assertionEnded( AssertionStats const& _assertionStats ) override {
|
||||
++counter;
|
||||
|
||||
stream << "# " << currentTestCaseInfo->name << std::endl;
|
||||
AssertionPrinter printer( stream, _assertionStats, counter );
|
||||
printer.print();
|
||||
stream << " # " << currentTestCaseInfo->name ;
|
||||
|
||||
stream << std::endl;
|
||||
return true;
|
||||
@ -205,16 +204,15 @@ namespace Catch {
|
||||
return;
|
||||
}
|
||||
|
||||
// using messages.end() directly (or auto) yields compilation error:
|
||||
std::vector<MessageInfo>::const_iterator itEnd = messages.end();
|
||||
const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
|
||||
const auto itEnd = messages.cend();
|
||||
const auto N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
|
||||
|
||||
{
|
||||
Colour colourGuard( colour );
|
||||
stream << " with " << pluralise( N, "message" ) << ":";
|
||||
}
|
||||
|
||||
for(; itMessage != itEnd; ) {
|
||||
while( itMessage != itEnd ) {
|
||||
// If this assertion is a warning ignore any INFO messages
|
||||
if( printInfoMessages || itMessage->type != ResultWas::Info ) {
|
||||
stream << " '" << itMessage->message << "'";
|
||||
@ -222,7 +220,9 @@ namespace Catch {
|
||||
Colour colourGuard( dimColour() );
|
||||
stream << " and";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
++itMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,10 +236,9 @@ namespace Catch {
|
||||
};
|
||||
|
||||
void printTotals( const Totals& totals ) const {
|
||||
stream << "1.." << totals.assertions.total();
|
||||
if( totals.testCases.total() == 0 ) {
|
||||
stream << "1..0 # Skipped: No tests ran.";
|
||||
} else {
|
||||
stream << "1.." << counter;
|
||||
stream << " # Skipped: No tests ran.";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -97,12 +97,12 @@ namespace Catch {
|
||||
case ResultWas::Ok:
|
||||
case ResultWas::Info:
|
||||
case ResultWas::Warning:
|
||||
throw std::domain_error( "Internal error in TeamCity reporter" );
|
||||
CATCH_ERROR( "Internal error in TeamCity reporter" );
|
||||
// These cases are here to prevent compiler warnings
|
||||
case ResultWas::Unknown:
|
||||
case ResultWas::FailureBit:
|
||||
case ResultWas::Exception:
|
||||
throw std::domain_error( "Not implemented" );
|
||||
CATCH_ERROR( "Not implemented" );
|
||||
}
|
||||
if( assertionStats.infoMessages.size() == 1 )
|
||||
msg << " with message:";
|
||||
@ -183,8 +183,7 @@ namespace Catch {
|
||||
|
||||
SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
|
||||
|
||||
if( !lineInfo.empty() )
|
||||
os << lineInfo << "\n";
|
||||
os << lineInfo << "\n";
|
||||
os << getLineOfChars<'.'>() << "\n\n";
|
||||
}
|
||||
|
||||
|
||||
271
include/uuid.h
271
include/uuid.h
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef STDUUID_H
|
||||
#define STDUUID_H
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
@ -15,10 +16,31 @@
|
||||
#include <chrono>
|
||||
#include <numeric>
|
||||
#include <atomic>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
# if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
|
||||
# define LIBUUID_CPP20_OR_GREATER
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef LIBUUID_CPP20_OR_GREATER
|
||||
#include <span>
|
||||
#else
|
||||
#include <gsl/span>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#ifdef UUID_SYSTEM_GENERATOR
|
||||
#include <objbase.h>
|
||||
#endif
|
||||
@ -55,19 +77,19 @@ namespace uuids
|
||||
namespace detail
|
||||
{
|
||||
template <typename TChar>
|
||||
constexpr inline unsigned char hex2char(TChar const ch)
|
||||
[[nodiscard]] constexpr inline unsigned char hex2char(TChar const ch) noexcept
|
||||
{
|
||||
if (ch >= static_cast<TChar>('0') && ch <= static_cast<TChar>('9'))
|
||||
return ch - static_cast<TChar>('0');
|
||||
return static_cast<unsigned char>(ch - static_cast<TChar>('0'));
|
||||
if (ch >= static_cast<TChar>('a') && ch <= static_cast<TChar>('f'))
|
||||
return 10 + ch - static_cast<TChar>('a');
|
||||
return static_cast<unsigned char>(10 + ch - static_cast<TChar>('a'));
|
||||
if (ch >= static_cast<TChar>('A') && ch <= static_cast<TChar>('F'))
|
||||
return 10 + ch - static_cast<TChar>('A');
|
||||
return static_cast<unsigned char>(10 + ch - static_cast<TChar>('A'));
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename TChar>
|
||||
constexpr inline bool is_hex(TChar const ch)
|
||||
[[nodiscard]] constexpr inline bool is_hex(TChar const ch) noexcept
|
||||
{
|
||||
return
|
||||
(ch >= static_cast<TChar>('0') && ch <= static_cast<TChar>('9')) ||
|
||||
@ -76,17 +98,18 @@ namespace uuids
|
||||
}
|
||||
|
||||
template <typename TChar>
|
||||
constexpr std::basic_string_view<TChar> to_string_view(TChar const * str)
|
||||
[[nodiscard]] constexpr std::basic_string_view<TChar> to_string_view(TChar const * str) noexcept
|
||||
{
|
||||
if (str) return str;
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename StringType>
|
||||
[[nodiscard]]
|
||||
constexpr std::basic_string_view<
|
||||
typename StringType::value_type,
|
||||
typename StringType::traits_type>
|
||||
to_string_view(StringType const & str)
|
||||
to_string_view(StringType const & str) noexcept
|
||||
{
|
||||
return str;
|
||||
}
|
||||
@ -99,14 +122,14 @@ namespace uuids
|
||||
|
||||
static constexpr unsigned int block_bytes = 64;
|
||||
|
||||
inline static uint32_t left_rotate(uint32_t value, size_t const count)
|
||||
[[nodiscard]] inline static uint32_t left_rotate(uint32_t value, size_t const count) noexcept
|
||||
{
|
||||
return (value << count) ^ (value >> (32 - count));
|
||||
}
|
||||
|
||||
sha1() { reset(); }
|
||||
|
||||
void reset()
|
||||
void reset() noexcept
|
||||
{
|
||||
m_digest[0] = 0x67452301;
|
||||
m_digest[1] = 0xEFCDAB89;
|
||||
@ -180,30 +203,30 @@ namespace uuids
|
||||
digest32_t d32;
|
||||
get_digest(d32);
|
||||
size_t di = 0;
|
||||
digest[di++] = (uint8_t)(d32[0] >> 24);
|
||||
digest[di++] = (uint8_t)(d32[0] >> 16);
|
||||
digest[di++] = (uint8_t)(d32[0] >> 8);
|
||||
digest[di++] = (uint8_t)(d32[0] >> 0);
|
||||
digest[di++] = static_cast<uint8_t>(d32[0] >> 24);
|
||||
digest[di++] = static_cast<uint8_t>(d32[0] >> 16);
|
||||
digest[di++] = static_cast<uint8_t>(d32[0] >> 8);
|
||||
digest[di++] = static_cast<uint8_t>(d32[0] >> 0);
|
||||
|
||||
digest[di++] = (uint8_t)(d32[1] >> 24);
|
||||
digest[di++] = (uint8_t)(d32[1] >> 16);
|
||||
digest[di++] = (uint8_t)(d32[1] >> 8);
|
||||
digest[di++] = (uint8_t)(d32[1] >> 0);
|
||||
digest[di++] = static_cast<uint8_t>(d32[1] >> 24);
|
||||
digest[di++] = static_cast<uint8_t>(d32[1] >> 16);
|
||||
digest[di++] = static_cast<uint8_t>(d32[1] >> 8);
|
||||
digest[di++] = static_cast<uint8_t>(d32[1] >> 0);
|
||||
|
||||
digest[di++] = (uint8_t)(d32[2] >> 24);
|
||||
digest[di++] = (uint8_t)(d32[2] >> 16);
|
||||
digest[di++] = (uint8_t)(d32[2] >> 8);
|
||||
digest[di++] = (uint8_t)(d32[2] >> 0);
|
||||
digest[di++] = static_cast<uint8_t>(d32[2] >> 24);
|
||||
digest[di++] = static_cast<uint8_t>(d32[2] >> 16);
|
||||
digest[di++] = static_cast<uint8_t>(d32[2] >> 8);
|
||||
digest[di++] = static_cast<uint8_t>(d32[2] >> 0);
|
||||
|
||||
digest[di++] = (uint8_t)(d32[3] >> 24);
|
||||
digest[di++] = (uint8_t)(d32[3] >> 16);
|
||||
digest[di++] = (uint8_t)(d32[3] >> 8);
|
||||
digest[di++] = (uint8_t)(d32[3] >> 0);
|
||||
digest[di++] = static_cast<uint8_t>(d32[3] >> 24);
|
||||
digest[di++] = static_cast<uint8_t>(d32[3] >> 16);
|
||||
digest[di++] = static_cast<uint8_t>(d32[3] >> 8);
|
||||
digest[di++] = static_cast<uint8_t>(d32[3] >> 0);
|
||||
|
||||
digest[di++] = (uint8_t)(d32[4] >> 24);
|
||||
digest[di++] = (uint8_t)(d32[4] >> 16);
|
||||
digest[di++] = (uint8_t)(d32[4] >> 8);
|
||||
digest[di++] = (uint8_t)(d32[4] >> 0);
|
||||
digest[di++] = static_cast<uint8_t>(d32[4] >> 24);
|
||||
digest[di++] = static_cast<uint8_t>(d32[4] >> 16);
|
||||
digest[di++] = static_cast<uint8_t>(d32[4] >> 8);
|
||||
digest[di++] = static_cast<uint8_t>(d32[4] >> 0);
|
||||
|
||||
return digest;
|
||||
}
|
||||
@ -213,10 +236,10 @@ namespace uuids
|
||||
{
|
||||
uint32_t w[80];
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
w[i] = (m_block[i * 4 + 0] << 24);
|
||||
w[i] |= (m_block[i * 4 + 1] << 16);
|
||||
w[i] |= (m_block[i * 4 + 2] << 8);
|
||||
w[i] |= (m_block[i * 4 + 3]);
|
||||
w[i] = static_cast<uint32_t>(m_block[i * 4 + 0] << 24);
|
||||
w[i] |= static_cast<uint32_t>(m_block[i * 4 + 1] << 16);
|
||||
w[i] |= static_cast<uint32_t>(m_block[i * 4 + 2] << 8);
|
||||
w[i] |= static_cast<uint32_t>(m_block[i * 4 + 3]);
|
||||
}
|
||||
for (size_t i = 16; i < 80; i++) {
|
||||
w[i] = left_rotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1);
|
||||
@ -270,6 +293,18 @@ namespace uuids
|
||||
size_t m_blockByteIndex;
|
||||
size_t m_byteCount;
|
||||
};
|
||||
|
||||
template <typename CharT>
|
||||
inline constexpr CharT empty_guid[37] = "00000000-0000-0000-0000-000000000000";
|
||||
|
||||
template <>
|
||||
inline constexpr wchar_t empty_guid<wchar_t>[37] = L"00000000-0000-0000-0000-000000000000";
|
||||
|
||||
template <typename CharT>
|
||||
inline constexpr CharT guid_encoder[17] = "0123456789abcdef";
|
||||
|
||||
template <>
|
||||
inline constexpr wchar_t guid_encoder<wchar_t>[17] = L"0123456789abcdef";
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------------
|
||||
@ -340,6 +375,13 @@ namespace uuids
|
||||
name_based_sha1 = 5 // The name-based version specified in RFS 4122 with SHA1 hashing
|
||||
};
|
||||
|
||||
// Forward declare uuid & to_string so that we can declare to_string as a friend later.
|
||||
class uuid;
|
||||
template <class CharT = char,
|
||||
class Traits = std::char_traits<CharT>,
|
||||
class Allocator = std::allocator<CharT>>
|
||||
std::basic_string<CharT, Traits, Allocator> to_string(uuid const &id);
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------------
|
||||
// uuid class
|
||||
// --------------------------------------------------------------------------------------------------------------------------
|
||||
@ -369,7 +411,7 @@ namespace uuids
|
||||
std::copy(first, last, std::begin(data));
|
||||
}
|
||||
|
||||
constexpr uuid_variant variant() const noexcept
|
||||
[[nodiscard]] constexpr uuid_variant variant() const noexcept
|
||||
{
|
||||
if ((data[8] & 0x80) == 0x00)
|
||||
return uuid_variant::ncs;
|
||||
@ -381,7 +423,7 @@ namespace uuids
|
||||
return uuid_variant::reserved;
|
||||
}
|
||||
|
||||
constexpr uuid_version version() const noexcept
|
||||
[[nodiscard]] constexpr uuid_version version() const noexcept
|
||||
{
|
||||
if ((data[6] & 0xF0) == 0x10)
|
||||
return uuid_version::time_based;
|
||||
@ -397,7 +439,7 @@ namespace uuids
|
||||
return uuid_version::none;
|
||||
}
|
||||
|
||||
constexpr bool is_nil() const noexcept
|
||||
[[nodiscard]] constexpr bool is_nil() const noexcept
|
||||
{
|
||||
for (size_t i = 0; i < data.size(); ++i) if (data[i] != 0) return false;
|
||||
return true;
|
||||
@ -408,17 +450,17 @@ namespace uuids
|
||||
data.swap(other.data);
|
||||
}
|
||||
|
||||
inline span<std::byte const, 16> as_bytes() const
|
||||
[[nodiscard]] inline span<std::byte const, 16> as_bytes() const
|
||||
{
|
||||
return span<std::byte const, 16>(reinterpret_cast<std::byte const*>(data.data()), 16);
|
||||
}
|
||||
|
||||
template <typename StringType>
|
||||
constexpr static bool is_valid_uuid(StringType const & in_str) noexcept
|
||||
[[nodiscard]] constexpr static bool is_valid_uuid(StringType const & in_str) noexcept
|
||||
{
|
||||
auto str = detail::to_string_view(in_str);
|
||||
bool firstDigit = true;
|
||||
int hasBraces = 0;
|
||||
size_t hasBraces = 0;
|
||||
size_t index = 0;
|
||||
|
||||
if (str.empty())
|
||||
@ -458,11 +500,11 @@ namespace uuids
|
||||
}
|
||||
|
||||
template <typename StringType>
|
||||
constexpr static std::optional<uuid> from_string(StringType const & in_str) noexcept
|
||||
[[nodiscard]] constexpr static std::optional<uuid> from_string(StringType const & in_str) noexcept
|
||||
{
|
||||
auto str = detail::to_string_view(in_str);
|
||||
bool firstDigit = true;
|
||||
int hasBraces = 0;
|
||||
size_t hasBraces = 0;
|
||||
size_t index = 0;
|
||||
|
||||
std::array<uint8_t, 16> data{ { 0 } };
|
||||
@ -485,12 +527,13 @@ namespace uuids
|
||||
|
||||
if (firstDigit)
|
||||
{
|
||||
data[index] = detail::hex2char(str[i]) << 4;
|
||||
data[index] = static_cast<uint8_t>(detail::hex2char(str[i]) << 4);
|
||||
firstDigit = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
data[index++] |= detail::hex2char(str[i]);
|
||||
data[index] = static_cast<uint8_t>(data[index] | detail::hex2char(str[i]));
|
||||
index++;
|
||||
firstDigit = true;
|
||||
}
|
||||
}
|
||||
@ -510,71 +553,59 @@ namespace uuids
|
||||
friend bool operator<(uuid const & lhs, uuid const & rhs) noexcept;
|
||||
|
||||
template <class Elem, class Traits>
|
||||
friend std::basic_ostream<Elem, Traits> & operator<<(std::basic_ostream<Elem, Traits> &s, uuid const & id);
|
||||
friend std::basic_ostream<Elem, Traits> & operator<<(std::basic_ostream<Elem, Traits> &s, uuid const & id);
|
||||
|
||||
template<class CharT, class Traits, class Allocator>
|
||||
friend std::basic_string<CharT, Traits, Allocator> to_string(uuid const& id);
|
||||
|
||||
friend std::hash<uuid>;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------------
|
||||
// operators and non-member functions
|
||||
// --------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
inline bool operator== (uuid const& lhs, uuid const& rhs) noexcept
|
||||
[[nodiscard]] inline bool operator== (uuid const& lhs, uuid const& rhs) noexcept
|
||||
{
|
||||
return lhs.data == rhs.data;
|
||||
}
|
||||
|
||||
inline bool operator!= (uuid const& lhs, uuid const& rhs) noexcept
|
||||
[[nodiscard]] inline bool operator!= (uuid const& lhs, uuid const& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
inline bool operator< (uuid const& lhs, uuid const& rhs) noexcept
|
||||
[[nodiscard]] inline bool operator< (uuid const& lhs, uuid const& rhs) noexcept
|
||||
{
|
||||
return lhs.data < rhs.data;
|
||||
}
|
||||
|
||||
template <class Elem, class Traits>
|
||||
std::basic_ostream<Elem, Traits> & operator<<(std::basic_ostream<Elem, Traits> &s, uuid const & id)
|
||||
template <class CharT,
|
||||
class Traits,
|
||||
class Allocator>
|
||||
[[nodiscard]] inline std::basic_string<CharT, Traits, Allocator> to_string(uuid const & id)
|
||||
{
|
||||
// save current flags
|
||||
std::ios_base::fmtflags f(s.flags());
|
||||
|
||||
// manipulate stream as needed
|
||||
s << std::hex << std::setfill(static_cast<Elem>('0'))
|
||||
<< std::setw(2) << (int)id.data[0]
|
||||
<< std::setw(2) << (int)id.data[1]
|
||||
<< std::setw(2) << (int)id.data[2]
|
||||
<< std::setw(2) << (int)id.data[3]
|
||||
<< '-'
|
||||
<< std::setw(2) << (int)id.data[4]
|
||||
<< std::setw(2) << (int)id.data[5]
|
||||
<< '-'
|
||||
<< std::setw(2) << (int)id.data[6]
|
||||
<< std::setw(2) << (int)id.data[7]
|
||||
<< '-'
|
||||
<< std::setw(2) << (int)id.data[8]
|
||||
<< std::setw(2) << (int)id.data[9]
|
||||
<< '-'
|
||||
<< std::setw(2) << (int)id.data[10]
|
||||
<< std::setw(2) << (int)id.data[11]
|
||||
<< std::setw(2) << (int)id.data[12]
|
||||
<< std::setw(2) << (int)id.data[13]
|
||||
<< std::setw(2) << (int)id.data[14]
|
||||
<< std::setw(2) << (int)id.data[15];
|
||||
std::basic_string<CharT, Traits, Allocator> uustr{detail::empty_guid<CharT>};
|
||||
|
||||
// restore original flags
|
||||
s.flags(f);
|
||||
|
||||
return s;
|
||||
for (size_t i = 0, index = 0; i < 36; ++i)
|
||||
{
|
||||
if (i == 8 || i == 13 || i == 18 || i == 23)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
uustr[i] = detail::guid_encoder<CharT>[id.data[index] >> 4 & 0x0f];
|
||||
uustr[++i] = detail::guid_encoder<CharT>[id.data[index] & 0x0f];
|
||||
index++;
|
||||
}
|
||||
|
||||
return uustr;
|
||||
}
|
||||
|
||||
template<class CharT = char,
|
||||
class Traits = std::char_traits<CharT>,
|
||||
class Allocator = std::allocator<CharT>>
|
||||
inline std::basic_string<CharT, Traits, Allocator> to_string(uuid const & id)
|
||||
template <class Elem, class Traits>
|
||||
std::basic_ostream<Elem, Traits>& operator<<(std::basic_ostream<Elem, Traits>& s, uuid const& id)
|
||||
{
|
||||
std::basic_stringstream<CharT, Traits, Allocator> sstr;
|
||||
sstr << id;
|
||||
return sstr.str();
|
||||
s << to_string(id);
|
||||
return s;
|
||||
}
|
||||
|
||||
inline void swap(uuids::uuid & lhs, uuids::uuid & rhs) noexcept
|
||||
@ -613,14 +644,19 @@ namespace uuids
|
||||
#ifdef _WIN32
|
||||
|
||||
GUID newId;
|
||||
::CoCreateGuid(&newId);
|
||||
HRESULT hr = ::CoCreateGuid(&newId);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
throw std::system_error(hr, std::system_category(), "CoCreateGuid failed");
|
||||
}
|
||||
|
||||
std::array<uint8_t, 16> bytes =
|
||||
{ {
|
||||
(unsigned char)((newId.Data1 >> 24) & 0xFF),
|
||||
(unsigned char)((newId.Data1 >> 16) & 0xFF),
|
||||
(unsigned char)((newId.Data1 >> 8) & 0xFF),
|
||||
(unsigned char)((newId.Data1) & 0xFF),
|
||||
static_cast<unsigned char>((newId.Data1 >> 24) & 0xFF),
|
||||
static_cast<unsigned char>((newId.Data1 >> 16) & 0xFF),
|
||||
static_cast<unsigned char>((newId.Data1 >> 8) & 0xFF),
|
||||
static_cast<unsigned char>((newId.Data1) & 0xFF),
|
||||
|
||||
(unsigned char)((newId.Data2 >> 8) & 0xFF),
|
||||
(unsigned char)((newId.Data2) & 0xFF),
|
||||
@ -700,7 +736,7 @@ namespace uuids
|
||||
#endif
|
||||
|
||||
template <typename UniformRandomNumberGenerator>
|
||||
class basic_uuid_random_generator
|
||||
class basic_uuid_random_generator
|
||||
{
|
||||
public:
|
||||
using engine_type = UniformRandomNumberGenerator;
|
||||
@ -710,9 +746,9 @@ namespace uuids
|
||||
explicit basic_uuid_random_generator(engine_type* gen) :
|
||||
generator(gen, [](auto) {}) {}
|
||||
|
||||
uuid operator()()
|
||||
[[nodiscard]] uuid operator()()
|
||||
{
|
||||
uint8_t bytes[16];
|
||||
alignas(uint32_t) uint8_t bytes[16];
|
||||
for (int i = 0; i < 16; i += 4)
|
||||
*reinterpret_cast<uint32_t*>(bytes + i) = distribution(*generator);
|
||||
|
||||
@ -742,7 +778,7 @@ namespace uuids
|
||||
{}
|
||||
|
||||
template <typename StringType>
|
||||
uuid operator()(StringType const & name)
|
||||
[[nodiscard]] uuid operator()(StringType const & name)
|
||||
{
|
||||
reset();
|
||||
process_characters(detail::to_string_view(name));
|
||||
@ -774,7 +810,7 @@ namespace uuids
|
||||
}
|
||||
}
|
||||
|
||||
uuid make_uuid()
|
||||
[[nodiscard]] uuid make_uuid()
|
||||
{
|
||||
detail::sha1::digest8_t digest;
|
||||
hasher.get_digest_bytes(digest);
|
||||
@ -804,7 +840,7 @@ namespace uuids
|
||||
|
||||
std::optional<mac_address> device_address;
|
||||
|
||||
bool get_mac_address()
|
||||
[[nodiscard]] bool get_mac_address()
|
||||
{
|
||||
if (device_address.has_value())
|
||||
{
|
||||
@ -827,7 +863,7 @@ namespace uuids
|
||||
return device_address.has_value();
|
||||
}
|
||||
|
||||
long long get_time_intervals()
|
||||
[[nodiscard]] long long get_time_intervals()
|
||||
{
|
||||
auto start = std::chrono::system_clock::from_time_t(time_t(-12219292800));
|
||||
auto diff = std::chrono::system_clock::now() - start;
|
||||
@ -835,7 +871,7 @@ namespace uuids
|
||||
return ns / 100;
|
||||
}
|
||||
|
||||
static unsigned short get_clock_sequence()
|
||||
[[nodiscard]] static unsigned short get_clock_sequence()
|
||||
{
|
||||
static std::mt19937 clock_gen(std::random_device{}());
|
||||
static std::uniform_int_distribution<unsigned short> clock_dis;
|
||||
@ -844,7 +880,7 @@ namespace uuids
|
||||
}
|
||||
|
||||
public:
|
||||
uuid operator()()
|
||||
[[nodiscard]] uuid operator()()
|
||||
{
|
||||
if (get_mac_address())
|
||||
{
|
||||
@ -889,10 +925,43 @@ namespace std
|
||||
using argument_type = uuids::uuid;
|
||||
using result_type = std::size_t;
|
||||
|
||||
result_type operator()(argument_type const &uuid) const
|
||||
[[nodiscard]] result_type operator()(argument_type const &uuid) const
|
||||
{
|
||||
#ifdef UUID_HASH_STRING_BASED
|
||||
std::hash<std::string> hasher;
|
||||
return static_cast<result_type>(hasher(uuids::to_string(uuid)));
|
||||
#else
|
||||
uint64_t l =
|
||||
static_cast<uint64_t>(uuid.data[0]) << 56 |
|
||||
static_cast<uint64_t>(uuid.data[1]) << 48 |
|
||||
static_cast<uint64_t>(uuid.data[2]) << 40 |
|
||||
static_cast<uint64_t>(uuid.data[3]) << 32 |
|
||||
static_cast<uint64_t>(uuid.data[4]) << 24 |
|
||||
static_cast<uint64_t>(uuid.data[5]) << 16 |
|
||||
static_cast<uint64_t>(uuid.data[6]) << 8 |
|
||||
static_cast<uint64_t>(uuid.data[7]);
|
||||
uint64_t h =
|
||||
static_cast<uint64_t>(uuid.data[8]) << 56 |
|
||||
static_cast<uint64_t>(uuid.data[9]) << 48 |
|
||||
static_cast<uint64_t>(uuid.data[10]) << 40 |
|
||||
static_cast<uint64_t>(uuid.data[11]) << 32 |
|
||||
static_cast<uint64_t>(uuid.data[12]) << 24 |
|
||||
static_cast<uint64_t>(uuid.data[13]) << 16 |
|
||||
static_cast<uint64_t>(uuid.data[14]) << 8 |
|
||||
static_cast<uint64_t>(uuid.data[15]);
|
||||
|
||||
if constexpr (sizeof(result_type) > 4)
|
||||
{
|
||||
return result_type(l ^ h);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64_t hash64 = l ^ h;
|
||||
return result_type(uint32_t(hash64 >> 32) ^ uint32_t(hash64));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* STDUUID_H */
|
||||
|
||||
@ -8,7 +8,7 @@ else ()
|
||||
set_target_properties(test_${PROJECT_NAME} PROPERTIES CXX_STANDARD 17)
|
||||
endif ()
|
||||
if (WIN32)
|
||||
target_compile_options(test_${PROJECT_NAME} PRIVATE /EHc)
|
||||
target_compile_options(test_${PROJECT_NAME} PRIVATE /EHc /Zc:hiddenFriend)
|
||||
target_compile_definitions(test_${PROJECT_NAME} PRIVATE _SCL_SECURE_NO_WARNINGS)
|
||||
elseif (APPLE)
|
||||
target_compile_options(test_${PROJECT_NAME} PRIVATE -fexceptions -g -Wall)
|
||||
@ -28,4 +28,4 @@ set_tests_properties("test_${PROJECT_NAME}"
|
||||
FAIL_REGULAR_EXPRESSION "Failed \\d+ test cases")
|
||||
set_tests_properties("test_${PROJECT_NAME}"
|
||||
PROPERTIES
|
||||
TIMEOUT 120)
|
||||
TIMEOUT 120)
|
||||
|
||||
@ -525,7 +525,11 @@ TEST_CASE("Test hashing", "[ops]")
|
||||
|
||||
auto h1 = std::hash<std::string>{};
|
||||
auto h2 = std::hash<uuid>{};
|
||||
#ifdef UUID_HASH_STRING_BASED
|
||||
REQUIRE(h1(str) == h2(guid));
|
||||
#else
|
||||
REQUIRE(h1(str) != h2(guid));
|
||||
#endif
|
||||
|
||||
auto engine = uuids::uuid_random_generator::engine_type{};
|
||||
seed_rng(engine);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user