revision based on LEWGI feedback from San Diego

This commit is contained in:
Marius Bancila 2019-01-08 16:46:19 +02:00
parent 0ebaceee2f
commit bc37fa250b
5 changed files with 677 additions and 576 deletions

262
P0959.md
View File

@ -1,8 +1,8 @@
| ___ | ___ |
| --- | --- |
| Doc. No.: | P0959R1 |
| Date: | 2018-09-05 |
| Reply to: | Marius Bancila |
| Date: | 2019-01-08 |
| Reply to: | Marius Bancila, Tony van Eerd |
| Audience: | Library WG |
| Title: | A Proposal for a Universally Unique Identifier Library |
@ -24,7 +24,7 @@ Revised with feedback from the LWG and the community.
* Added a conversion construct from `std::span<std::byte, 16>`.
* Added the member function `as_bytes()` to convert the `uuid` into a view of its underlying bytes.
* Constructing a `uuid` from a range with a size other than 16 is undefined behaviour.
* Replaced operators '==', '!=' and `<` with the three-way operator `<=>`
* Replaced operators `==`, `!=` and `<` with the three-way operator `<=>`
* Removed mutable iterators (but preserved the constant iterators).
* Removed typedefs and others container-like parts.
* Defined the correlation between the internal UUID bytes and the string representation.
@ -33,6 +33,62 @@ Revised with feedback from the LWG and the community.
* Most functions are constexpr.
* Replaced typedefs with using statements.
### 1.3 P0959R2
P0959R1 was discussed in San Diego LEWGI (attendance 13 people) with the following feedback in polls:
* `std::uuid`'s byte ordering should be fixed (implying we have the option to make it memcpy and there is no need for a container-like interface)
| SF | F | N | A | SA |
|---|---|---|---|---|
| 4 | 3 | 1 | 0 | 0 |
* `std::uuid` should have iterators (e.g. begin/end methods)
| SF | F | N | A | SA |
|---|---|---|---|---|
| 2 | 3 | 2 | 1 | 1 |
*Comment*: interpreted as "author's discretion".
* `std::uuid` should be able to generate uuids from names
| SF | F | N | A | SA |
|---|---|---|---|---|
| 4 | 2 | 3 | 0 | 0 |
* explore having `std::uuid` be able to generate from times
| SF | F | N | A | SA |
|---|---|---|---|---|
| 4 | 3 | 2 | 0 | 0 |
* We want `std::basic_uuid_random_generator`
| SF | F | N | A | SA |
|---|---|---|---|---|
| 0 | 6 | 2 | 0 | 1 |
*Comments*: Author is instructed to investigate less hazardous API, and not just fix the examples.
* Explore non-exception based approach to error handling in `std::uuid::from_string`
| SF | F | N | A | SA |
|---|---|---|---|---|
| 9 | 0 | 0 | 0 | 0 |
Based on this feedback the following changes have been done in this version:
* `std::uuid` byte order is fixed (TODO: formal specification)
* removed container interface (iterators and `size()`) because, fundamentally, a uuid is a single entity, an identifier, and not a container of other things
* removed `basic_uuid_random_generator` default constructor
* added `uuid_time_generator` functor to generate variant 1 time-based uuids
* proper initialization for the pseudo-random generator engines in all examples
* removed `to_wstring()` and made `to_string()` a function template
* made `from_string()` a non-throwing function template
* added `is_valid_uuid()` a non-throwing function template that checks if a string contains is a valid uuid
* `uuid`s produced from names in different character sets or encodings are different (i.e. "jane" and L"jane")
* removed the class `uuid_error`
* footnote on the use of the name Microsoft
## II. Motivation
@ -51,9 +107,8 @@ This proposal is a pure library extension. It does not require changes to any st
The proposed library, that should be available in a new header called `<uuid>` in the namespace `std`, provides:
* a class called `uuid` that represents a universally unique identifier
* strongly type enums `uuid_variant` and `uuid_version` to represent the possible variant and version types of a UUID
* a class called `uuid_error` representing an exception type for `uuid` operations
* function objects that generate UUIDs, called generators: `basic_uuid_random_generator<T>`, `uuid_random_generator`, `uuid_name_generator`
* conversion functions from strings `from_string()` and to strings `std::to_string()` \ `std::to_wstring()`, as well as an overloaded `operator<<` for `std::basic_ostream`
* function objects that generate UUIDs, called generators: `basic_uuid_random_generator<T>`, `uuid_random_generator`, `uuid_name_generator`, `uuid_time_generator`
* conversion functions from strings `from_string()` and to strings `std::to_string()`, as well as an overloaded `operator<<` for `std::basic_ostream`
* `std::swap()` overload for `uuid`
* `std::hash<>` specialization for `uuid`
@ -111,14 +166,6 @@ uuid id{ data };
assert(to_string(id) == "47183823-2574-4bfd-b411-99ed177d3e43");
```
### Size
Member function `size()` indicates the number of bytes in the UUID. Because this is a fixed size structure this function always returns 16.
```cpp
uuid id;
assert(id.size() == 16);
```
### Nil
A nil UUID is a special UUID that has all the bits set to 0. Its canonical textual representation is `00000000-0000-0000-0000-000000000000`. Member function `is_nil()` indicates whether the `uuid` has all the bits set to 0. A nil UUID is created by the default constructor or by parsing the strings `00000000-0000-0000-0000-000000000000` or `{00000000-0000-0000-0000-000000000000}`.
@ -127,33 +174,10 @@ A nil UUID is a special UUID that has all the bits set to 0. Its canonical textu
uuid id;
assert(id.is_nil());
uuid id = uuid::from_string("00000000-0000-0000-0000-000000000000");
assert(id.is_nil());
std::optional<uuid> id = uuid::from_string("00000000-0000-0000-0000-000000000000");
assert(id.has_value() && id.value().is_nil());
```
### Iterators
Constant iterators allow direct access to the underlaying `uuid` data. This enables both direct reading of the `uuid` bits. The `uuid` class has const `begin()` and `end()` members to return constant iterators to the first and the one-past-last element of the UUID data.
```cpp
std::array<uuid::value_type, 16> arr{{
0x47, 0x18, 0x38, 0x23,
0x25, 0x74,
0x4b, 0xfd,
0xb4, 0x11,
0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43
}};
uuid id(std::begin(arr), std::end(arr));
assert(to_string(id) == "47183823-2574-4bfd-b411-99ed177d3e43");
size_t i = 0;
for (auto const & b : id)
assert(arr[i++] == b);
```
Because the internal representation may not be a straightforward array of bytes and may have arbitrary endianness iterators are not defined as pointers.
### `variant` and `version`
Member functions `variant()` and `version()` allow checking the variant type of the uuid and, respectively, the version type. These are defined by two strongly typed enums called `uuid_variant` and `uuid_version`.
@ -178,12 +202,12 @@ std::array<uuid::value_type, 16> arr{ {
} };
uuid id1{ std::span<uuid::value_type, 16>{arr} };
uuid id2 = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43");
uuid id2 = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value();
assert(id1 == id2);
std::set<std::uuid> ids{
uuid{},
uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43")
uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()
};
assert(ids.size() == 2);
@ -201,7 +225,7 @@ std::array<uuids::uuid::value_type, 16> arr{ {
0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43
} };
const uuid id{ arr };
uuid const id{ arr };
assert(!id.is_nil());
auto view = id.as_bytes();
@ -214,7 +238,7 @@ Both member and non-member `swap()` functions are available to perform the swapp
```cpp
uuid empty;
uuid id = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43");
uuid id = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value();
assert(empty.is_nil());
assert(!id.is_nil());
@ -232,25 +256,29 @@ assert(!id.is_nil());
### String parsing
Static overloaded member function `from_string()` allows to create `uuid` instances from various strings.
Static overloaded member function template `from_string()` allows to create `uuid` instances from various strings.
The input argument must have the form `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` or `{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}` where `x` is a hexadecimal digit. Should the argument be of a different format, an `uuid_error` exception is thrown.
The input argument must have the form `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` or `{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}` where `x` is a hexadecimal digit. The return value is an `std::optional<uuid>` that contains a valid `uuid` if the parsing completed successful.
```cpp
auto id1 = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43");
assert(id1.has_value());
auto id2 = uuid::from_string(L"{47183823-2574-4bfd-b411-99ed177d3e43}");
assert(id2.has_value());
```
The order of the bytes in the input string reflects directly into the internal representation of the UUID. That is, for an input string in the form `"aabbccdd-eeff-gghh-iijj-kkllmmnnoopp"` or `"{aabbccdd-eeff-gghh-iijj-kkllmmnnoopp}"` the internal byte order of the resulted UUID is `aa,bb,cc,dd,ee,ff,gg,hh,ii,jj,kk,ll,mm,nn,oo,pp`.
### String conversion
Non-member functions `to_string()` and `to_wstring()` return a string with the UUID formatted to the canonical textual representation `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`, where `x` is a lower case hexadecimal digit.
Non-member template function `to_string()` returns a string with the UUID formatted to the canonical textual representation `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`, where `x` is a lower case hexadecimal digit.
```cpp
uuid id = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43");
assert(to_string(id) == "47183823-2574-4bfd-b411-99ed177d3e43");
assert(to_wstring(id) == L"47183823-2574-4bfd-b411-99ed177d3e43");
auto id = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43");
assert(id.has_value());
assert(to_string(id.value()) == "47183823-2574-4bfd-b411-99ed177d3e43");
assert(to_string<wchar_t>(id.value()) == L"47183823-2574-4bfd-b411-99ed177d3e43");
```
The order of the internal UUID bytes reflects directly into the string bytes order. That is, for a UUID with the internal bytes in the form `aa,bb,cc,dd,ee,ff,gg,hh,ii,jj,kk,ll,mm,nn,oo,pp` the resulted string has the form `"aabbccdd-eeff-gghh-iijj-kkllmmnnoopp"`.
@ -262,7 +290,7 @@ A `std::hash<>` specialization for `uuid` is provided in order to enable the use
```cpp
std::unordered_set<uuid> ids{
uuid{},
uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43"),
uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value(),
};
assert(ids.size() == 2);
@ -275,37 +303,43 @@ Several function objects, called generators, are provided in order to create dif
Examples for generating new UUIDs with the `basic_uuid_random_generator` class:
```cpp
{
basic_uuid_random_generator<std::mt19937> dgen;
auto id1 = dgen();
assert(!id1.is_nil());
assert(id1.size() == 16);
assert(id1.version() == uuid_version::random_number_based);
assert(id1.variant() == uuid_variant::rfc);
}
{
basic_uuid_random_generator<std::ranlux48_base> dgen;
std::random_device rd;
auto seed_data = std::array<int, std::mt19937::state_size> {};
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
std::mt19937 engine(seq);
basic_uuid_random_generator<std::mt19937> dgen{engine};
auto id1 = dgen();
assert(!id1.is_nil());
assert(id1.size() == 16);
assert(id1.version() == uuid_version::random_number_based);
assert(id1.variant() == uuid_variant::rfc);
}
{
std::random_device rd;
std::ranlux48_base generator(rd());
auto seed_data = std::array<int, 6> {};
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
std::ranlux48_base engine(seq);
basic_uuid_random_generator<std::ranlux48_base> dgen(&generator);
basic_uuid_random_generator<std::ranlux48_base> dgen{engine};
auto id1 = dgen();
assert(!id1.is_nil());
assert(id1.size() == 16);
assert(id1.version() == uuid_version::random_number_based);
assert(id1.variant() == uuid_variant::rfc);
}
{
std::random_device rd;
auto generator = std::make_unique<std::mt19937>(rd());
auto seed_data = std::array<int, std::mt19937::state_size> {};
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
auto engine = std::make_unique<std::mt19937>(seq);
basic_uuid_random_generator<std::mt19937> dgen(generator.get());
basic_uuid_random_generator<std::mt19937> dgen(engine.get());
auto id1 = dgen();
assert(!id1.is_nil());
assert(id1.size() == 16);
@ -313,18 +347,26 @@ Examples for generating new UUIDs with the `basic_uuid_random_generator` class:
assert(id1.variant() == uuid_variant::rfc);
}
```
Examples for generating new UUIDs with the `uuid_random_generator` type alias:
```cpp
uuid_random_generator dgen;
std::random_device rd;
auto seed_data = std::array<int, std::mt19937::state_size> {};
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
std::mt19937 engine(seq);
uuid const guid = uuid_random_generator{engine}();
auto id1 = dgen();
assert(!id1.is_nil());
assert(id1.size() == 16);
assert(id1.version() == uuid_version::random_number_based);
assert(id1.variant() == uuid_variant::rfc);
```
Examples for genearting new UUIDs with the `uuid_name_generator` class:
```cpp
uuid_name_generator dgen(uuid::from_string("415ccc2b-f5cf-4ec1-b544-45132a518cc8"));
uuid_name_generator dgen(uuid::from_string("415ccc2b-f5cf-4ec1-b544-45132a518cc8").value());
auto id1 = dgen("john");
assert(!id1.is_nil());
assert(id1.size() == 16);
@ -373,6 +415,8 @@ namespace std {
}
```
**Footnote**: Microsoft is a registered trademark of Microsoft Corporation. This information is given for the convenience of users of this document and does not constitute an endorsement by ISO or IEC of these products.
### `uuid_version` enum
```cpp
@ -389,17 +433,6 @@ namespace std {
}
```
### `uuid_error` class
```cpp
namespace std {
struct uuid_error : public std::runtime_error
{
explicit uuid_error(std::string_view message);
explicit uuid_error(char const * message);
};
}
```
### `uuid` class
```cpp
@ -407,7 +440,6 @@ namespace std {
struct uuid
{
using value_type = uint8_t;
using const_iterator = /*implementation-defined*/;
constexpr uuid() noexcept = default;
@ -418,23 +450,29 @@ namespace std {
constexpr uuid_variant variant() const noexcept;
constexpr uuid_version version() const noexcept;
constexpr std::size_t size() const noexcept;
constexpr bool is_nil() const noexcept;
constexpr void swap(uuid & other) noexcept;
constexpr const_iterator begin() const noexcept;
constexpr const_iterator end() const noexcept;
constexpr std::span<std::byte const, 16> as_bytes() const;
constexpr std::strong_ordering operator<=>(uuid const&) const noexcept = default;
template <typename TChar>
static uuid from_string(TChar const * const str, size_t const size);
static uuid from_string(std::string_view str);
static uuid from_string(std::wstring_view str);
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>& str) noexcept;
template<class CharT = char>
static uuid from_string(CharT const * str);
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>& str);
private:
template <class Elem, class Traits>
friend std::basic_ostream<Elem, Traits> & operator<<(std::basic_ostream<Elem, Traits> &s, uuid const & id);
@ -451,8 +489,10 @@ namespace std {
template <class Elem, class Traits>
std::basic_ostream<Elem, Traits> & operator<<(std::basic_ostream<Elem, Traits> &s, uuid const & id);
template<class CharT = char,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>>
inline std::string to_string(uuid const & id);
inline std::wstring to_wstring(uuid const & id);
}
```
@ -464,11 +504,10 @@ namespace std {
class basic_uuid_random_generator
{
public:
using result_type = uuid;
using engine_type = UniformRandomNumberGenerator;
basic_uuid_random_generator();
explicit basic_uuid_random_generator(UniformRandomNumberGenerator& gen);
explicit basic_uuid_random_generator(UniformRandomNumberGenerator* gen);
explicit basic_uuid_random_generator(engine_type& gen);
explicit basic_uuid_random_generator(engine_type* gen);
uuid operator()();
};
@ -481,13 +520,13 @@ namespace std {
}
```
`uuid_name_generator` is a function object that generates new UUIDs from a name. It has to be initialized with another UUID and has overloaded `operator()` for both `std::string_view` and `std::wstring_view`.
This generator produces different uuids for the same text represented in different character sets or encodings. In order words, the uuids generated from "jane" and L"jane" are different.
```cpp
namespace std {
class uuid_name_generator
{
public:
using result_type = uuid;
explicit uuid_name_generator(uuid const& namespace_uuid) noexcept;
uuid operator()(std::string_view name);
@ -496,6 +535,22 @@ namespace std {
}
```
`uuid_time_generator` is a function object that generates a time-based UUID as described in the RFC4122 document. The generated uuid's parts are as follows:
* the timestamp is a 60-bit value, representing the number of 100 nanosecond intervals since 15 October 1582 00:00:000000000.
* the clock sequence is a 14-bit value, that is initially a high-quality pseudo-random value; when the previous value is known, it is simply incremented by one.
* the node identifier is a IEEE 802 MAC address (when multiple are available any could be used); if no such address is available, a pseudo-randomly generated value may be used, in which case the multicast bit (least significant bit of the first byte) is set to 1, this to avoid clashes with legitimate IEEE 802 addresses.
```cpp
namespace std {
class uuid_time_generator
{
public:
uuid_time_generator() noexcept;
uuid operator()();
};
}
```
### Specialization
The template specializations of `std::hash` for the `uuid` class allow users to obtain hashes of UUIDs.
```cpp
@ -534,7 +589,6 @@ namespace std {
struct uuid
{
using value_type = uint8_t;
using const_iterator = /*implementation-defined*/;
constexpr uuid() noexcept = default;
@ -545,21 +599,27 @@ namespace std {
constexpr uuid_variant variant() const noexcept;
constexpr uuid_version version() const noexcept;
constexpr std::size_t size() const noexcept;
constexpr bool is_nil() const noexcept;
constexpr void swap(uuid & other) noexcept;
constexpr const_iterator begin() const noexcept;
constexpr const_iterator end() const noexcept;
constexpr std::span<std::byte const, 16> as_bytes() const;
template <typename TChar>
static uuid from_string(TChar const * const str, size_t const size);
static uuid from_string(std::string_view str);
static uuid from_string(std::wstring_view str);
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>& str) noexcept;
template<class CharT = char>
static uuid from_string(CharT const * str);
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>& str);
private:
template <class Elem, class Traits>
friend std::basic_ostream<Elem, Traits> & operator<<(std::basic_ostream<Elem, Traits> &s, uuid const & id);

View File

@ -18,16 +18,15 @@ Basic types:
| `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`. |
| `uuid_variant` | a strongly type enum representing the type of a UUID |
| `uuid_version` | a strongly type enum representing the version of a UUID |
| `uuid_error` | a class representing an exception type for `uuid` operations |
Generators:
| Name | Description |
| ---- | ----------- |
| `uuid_system_generator` | a function object that generates new UUIDs using operating system resources (`CoCreateGuid` on Windows, `uuid_generate` on Linux, `CFUUIDCreate` on Mac) |
| `basic_uuid_random_generator` | a function object that generates version 4 UUIDs using a pseudo-random number generator engine. |
| `uuid_random_generator` | a basic_uuid_random_generator using the Marsenne Twister engine, i.e. `basic_uuid_random_generator<std::mt19937>` |
| `uuid_random_generator` | a `basic_uuid_random_generator` using the Marsenne 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. |
Utilities:
@ -43,13 +42,14 @@ Other:
| `operator==` and `operator!=` | for UUIDs comparison for equality/inequality |
| `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. |
| `operator<<` | to write a UUID to an output stream using the canonical textual representation. |
| `to_string()` | creates a `std::string` with the canonical textual representation of a UUID. |
| `to_wstring()` | creates a `std::wstring` with the canonical textual representation of a UUID. |
This project is currently under development and should be ignored until further notice.
| `to_string()` | creates a string with the canonical textual representation of a UUID. |
## Library history
This library is an implementation of the proposal [P0959](P0959.md). As the proposal evolves based on the standard commity and the C++ community feedback, this library implementation will reflect those changes. See the revision history of the proposal for history of changes.
This library is an implementation of the proposal [P0959](P0959.md).
**As the proposal evolves based on the standard commity and the C++ community feedback, this library implementation will reflect those changes.**
See the revision history of the proposal for history of changes.
## Using the library
The following is a list of examples for using the library:
@ -58,7 +58,6 @@ The following is a list of examples for using the library:
```cpp
uuid empty;
assert(empty.is_nil());
assert(empty.size() == 16);
```
* Creating a new UUID
@ -66,7 +65,6 @@ assert(empty.size() == 16);
```cpp
uuid const id = uuids::uuid_system_generator{}();
assert(!id.is_nil());
assert(id.size() == 16);
assert(id.version() == uuids::uuid_version::random_number_based);
assert(id.variant() == uuids::uuid_variant::rfc);
```
@ -74,7 +72,13 @@ assert(id.variant() == uuids::uuid_variant::rfc);
* Creating a new UUID with a default random generator
```cpp
uuids::uuid_random_generator gen;
std::random_device rd;
auto seed_data = std::array<int, std::mt19937::state_size> {};
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
std::mt19937 generator(seq);
uuid const guid = uuids::uuid_random_generator{generator}();
uuid const id = gen();
assert(!id.is_nil());
assert(id.size() == 16);
@ -86,7 +90,11 @@ assert(id.variant() == uuids::uuid_variant::rfc);
```cpp
std::random_device rd;
std::ranlux48_base generator(rd());
auto seed_data = std::array<int, 6> {};
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
std::ranlux48_base generator(seq);
uuids::basic_uuid_random_generator<std::ranlux48_base> gen(&generator);
uuid const id = gen();
assert(!id.is_nil());
@ -98,11 +106,10 @@ assert(id.variant() == uuids::uuid_variant::rfc);
* Creating a new UUID with the name generator
```cpp
uuids::uuid_name_generator gen;
uuids::uuid_name_generator gen(uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value());
uuid const id = gen();
assert(!id.is_nil());
assert(id.size() == 16);
assert(id.version() == uuids::uuid_version::name_based_sha1);
assert(id.variant() == uuids::uuid_variant::rfc);
```
@ -113,16 +120,17 @@ assert(id.variant() == uuids::uuid_variant::rfc);
using namespace std::string_literals;
auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s;
uuid id(uuids::uuid::from_string(str));
assert(uuids::to_string(id) == str);
auto id = uuids::uuid::from_string(str);
assert(id.has_value());
assert(uuids::to_string(id.value()) == str);
// or
uuid id(uuids::uuid::from_string(L"{47183823-2574-4bfd-b411-99ed177d3e43}"s));
assert(id.wstring() == str);
uuid id = uuids::uuid::from_string(L"{47183823-2574-4bfd-b411-99ed177d3e43}"s).value();
assert(uuids::to_string<wchar_t>(id.value()) == str);
```
* Creating a UUID from sequence of 16 bytes
* Creating a UUID from a sequence of 16 bytes
```cpp
std::array<uuids::uuid::value_type, 16> arr{{
@ -146,6 +154,7 @@ uuids::uuid::value_type arr[16] = {
uuid id(std::begin(arr), std::end(arr));
assert(uuids::to_string(id) == "47183823-2574-4bfd-b411-99ed177d3e43");
```
* Comparing UUIDs
```cpp
@ -175,35 +184,25 @@ empty.swap(id);
assert(empty.is_nil());
assert(!id.is_nil());
```
* Converting to string
```cpp
uuid empty;
assert(uuids::to_string(empty) == "00000000-0000-0000-0000-000000000000");
assert(uuids::to_wstring(empty) == L"00000000-0000-0000-0000-000000000000");
```
* Iterating through the UUID data
```cpp
std::array<uuids::uuid::value_type, 16> arr{{
0x47, 0x18, 0x38, 0x23,
0x25, 0x74,
0x4b, 0xfd,
0xb4, 0x11,
0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43}};
uuid id(arr);
size_t i = 0;
for (auto const & b : id)
assert(arr[i++] == b);
assert(uuids::to_string<wchar_t>(empty) == L"00000000-0000-0000-0000-000000000000");
```
* Using with an orderered associative container
```cpp
uuids::uuid_random_generator gen;
std::random_device rd;
auto seed_data = std::array<int, std::mt19937::state_size> {};
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
std::mt19937 engine(seq);
uuids::uuid_random_generator gen(&engine);
std::set<uuids::uuid> ids{uuid{}, gen(), gen(), gen(), gen()};
assert(ids.size() == 5);
@ -213,7 +212,13 @@ assert(ids.find(uuid{}) != ids.end());
* Using in an unordered associative container
```cpp
uuids::uuid_random_generator gen;
std::random_device rd;
auto seed_data = std::array<int, std::mt19937::state_size> {};
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
std::mt19937 engine(seq);
uuids::uuid_random_generator gen(&engine);
std::unordered_set<uuids::uuid> ids{uuid{}, gen(), gen(), gen(), gen()};
assert(ids.size() == 5);
@ -225,7 +230,7 @@ assert(ids.find(uuid{}) != ids.end());
```cpp
using namespace std::string_literals;
auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s;
uuid id(uuids::uuid::from_string(str));
uuid id = uuids::uuid::from_string(str).value();
auto h1 = std::hash<std::string>{};
auto h2 = std::hash<uuid>{};

View File

@ -11,6 +11,7 @@
#include <memory>
#include <functional>
#include <type_traits>
#include <optional>
#include <assert.h>
#include <gsl/span>
@ -283,19 +284,6 @@ namespace uuids
reserved
};
struct uuid_error : public std::runtime_error
{
explicit uuid_error(std::string_view message)
: std::runtime_error(message.data())
{
}
explicit uuid_error(char const * message)
: std::runtime_error(message)
{
}
};
// indicated by a bit pattern in octet 6, marked with M in xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx
enum class uuid_version
{
@ -309,151 +297,6 @@ namespace uuids
struct uuid
{
struct uuid_const_iterator
{
using self_type = uuid_const_iterator;
using value_type = uint8_t;
using reference = uint8_t const &;
using pointer = uint8_t const *;
using iterator_category = std::random_access_iterator_tag;
using difference_type = ptrdiff_t;
protected:
pointer ptr = nullptr;
size_t index = 0;
bool compatible(self_type const & other) const noexcept
{
return ptr == other.ptr;
}
public:
constexpr explicit uuid_const_iterator(pointer ptr, size_t const index) :
ptr(ptr), index(index)
{
}
uuid_const_iterator(uuid_const_iterator const & o) = default;
uuid_const_iterator& operator=(uuid_const_iterator const & o) = default;
~uuid_const_iterator() = default;
self_type & operator++ ()
{
if (index >= 16)
throw std::out_of_range("Iterator cannot be incremented past the end of the data.");
++index;
return *this;
}
self_type operator++ (int)
{
self_type tmp = *this;
++*this;
return tmp;
}
bool operator== (self_type const & other) const
{
assert(compatible(other));
return index == other.index;
}
bool operator!= (self_type const & other) const
{
return !(*this == other);
}
reference operator* () const
{
if (ptr == nullptr)
throw std::bad_function_call();
return *(ptr + index);
}
reference operator-> () const
{
if (ptr == nullptr)
throw std::bad_function_call();
return *(ptr + index);
}
uuid_const_iterator() = default;
self_type & operator--()
{
if (index <= 0)
throw std::out_of_range("Iterator cannot be decremented past the beginning of the data.");
--index;
return *this;
}
self_type operator--(int)
{
self_type tmp = *this;
--*this;
return tmp;
}
self_type operator+(difference_type offset) const
{
self_type tmp = *this;
return tmp += offset;
}
self_type operator-(difference_type offset) const
{
self_type tmp = *this;
return tmp -= offset;
}
difference_type operator-(self_type const & other) const
{
assert(compatible(other));
return (index - other.index);
}
bool operator<(self_type const & other) const
{
assert(compatible(other));
return index < other.index;
}
bool operator>(self_type const & other) const
{
return other < *this;
}
bool operator<=(self_type const & other) const
{
return !(other < *this);
}
bool operator>=(self_type const & other) const
{
return !(*this < other);
}
self_type & operator+=(difference_type const offset)
{
if (static_cast<difference_type>(index) + offset < 0 ||
static_cast<difference_type>(index) + offset > 16)
throw std::out_of_range("Iterator cannot be incremented outside data bounds.");
index += offset;
return *this;
}
self_type & operator-=(difference_type const offset)
{
return *this += -offset;
}
value_type const & operator[](difference_type const offset) const
{
return (*(*this + offset));
}
};
using value_type = uint8_t;
public:
@ -499,8 +342,6 @@ namespace uuids
return uuid_version::none;
}
constexpr std::size_t size() const noexcept { return 16; }
constexpr bool is_nil() const noexcept
{
for (size_t i = 0; i < data.size(); ++i) if (data[i] != 0) return false;
@ -512,38 +353,97 @@ namespace uuids
data.swap(other.data);
}
constexpr uuid_const_iterator begin() const noexcept { return uuid_const_iterator(&data[0], 0); }
constexpr uuid_const_iterator end() const noexcept { return uuid_const_iterator(&data[0], 16); }
inline gsl::span<std::byte const, 16> as_bytes() const
{
return gsl::span<std::byte const, 16>(reinterpret_cast<std::byte const*>(data.data()), 16);
}
template <typename TChar>
static uuid from_string(TChar const * const str, size_t const size)
template<class CharT = char>
static bool is_valid_uuid(CharT const * str) noexcept
{
TChar digit = 0;
CharT digit = 0;
bool firstDigit = true;
int hasBraces = 0;
size_t index = 0;
std::array<uint8_t, 16> data{ { 0 } };
size_t size = 0;
if constexpr(std::is_same_v<CharT, char>)
size = strlen(str);
else
size = wcslen(str);
if (str == nullptr || size == 0)
throw uuid_error{ "Wrong uuid format" };
return false;
if (str[0] == static_cast<TChar>('{'))
if (str[0] == static_cast<CharT>('{'))
hasBraces = 1;
if (hasBraces && str[size - 1] != static_cast<TChar>('}'))
throw uuid_error{ "Wrong uuid format" };
if (hasBraces && str[size - 1] != static_cast<CharT>('}'))
return false;
for (size_t i = hasBraces; i < size - hasBraces; ++i)
{
if (str[i] == static_cast<TChar>('-')) continue;
if (str[i] == static_cast<CharT>('-')) continue;
if (index >= 16 || !detail::is_hex(str[i]))
{
throw uuid_error{ "Wrong uuid format" };
return false;
}
if (firstDigit)
{
firstDigit = false;
}
else
{
index++;
firstDigit = true;
}
}
if (index < 16)
{
return false;
}
return true;
}
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>& str) noexcept
{
return is_valid_uuid(str.c_str());
}
template<class CharT = char>
static std::optional<uuid> from_string(CharT const * str) noexcept
{
CharT digit = 0;
bool firstDigit = true;
int hasBraces = 0;
size_t index = 0;
size_t size = 0;
if constexpr(std::is_same_v<CharT, char>)
size = strlen(str);
else
size = wcslen(str);
std::array<uint8_t, 16> data{ { 0 } };
if (str == nullptr || size == 0) return {};
if (str[0] == static_cast<CharT>('{'))
hasBraces = 1;
if (hasBraces && str[size - 1] != static_cast<CharT>('}'))
return {};
for (size_t i = hasBraces; i < size - hasBraces; ++i)
{
if (str[i] == static_cast<CharT>('-')) continue;
if (index >= 16 || !detail::is_hex(str[i]))
{
return {};
}
if (firstDigit)
@ -560,20 +460,18 @@ namespace uuids
if (index < 16)
{
throw uuid_error{ "Wrong uuid format" };
return {};
}
return uuid{ std::cbegin(data), std::cend(data) };
}
static uuid from_string(std::string_view str)
template<class CharT = char,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>>
static std::optional<uuid> from_string(std::basic_string<CharT, Traits, Allocator>& str) noexcept
{
return from_string(str.data(), str.size());
}
static uuid from_string(std::wstring_view str)
{
return from_string(str.data(), str.size());
return from_string(str.c_str());
}
private:
@ -627,16 +525,12 @@ namespace uuids
<< std::setw(2) << (int)id.data[15];
}
inline std::string to_string(uuid const & id)
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)
{
std::stringstream sstr;
sstr << id;
return sstr.str();
}
inline std::wstring to_wstring(uuid const & id)
{
std::wstringstream sstr;
std::basic_stringstream<CharT, Traits, Allocator> sstr;
sstr << id;
return sstr.str();
}
@ -745,18 +639,11 @@ namespace uuids
class basic_uuid_random_generator
{
public:
using result_type = uuid;
using engine_type = UniformRandomNumberGenerator;
basic_uuid_random_generator()
:generator(new UniformRandomNumberGenerator)
{
std::random_device rd;
generator->seed(rd());
}
explicit basic_uuid_random_generator(UniformRandomNumberGenerator& gen) :
explicit basic_uuid_random_generator(engine_type& gen) :
generator(&gen, [](auto) {}) {}
explicit basic_uuid_random_generator(UniformRandomNumberGenerator* gen) :
explicit basic_uuid_random_generator(engine_type* gen) :
generator(gen, [](auto) {}) {}
uuid operator()()
@ -786,8 +673,6 @@ namespace uuids
class uuid_name_generator
{
public:
using result_type = uuid;
explicit uuid_name_generator(uuid const& namespace_uuid) noexcept
: nsuuid(namespace_uuid)
{}
@ -810,8 +695,9 @@ namespace uuids
void reset()
{
hasher.reset();
uint8_t bytes[16];
std::copy(std::begin(nsuuid), std::end(nsuuid), bytes);
std::byte bytes[16];
auto nsbytes = nsuuid.as_bytes();
std::copy(std::cbegin(nsbytes), std::cend(nsbytes), bytes);
hasher.process_bytes(bytes, 16);
}

View File

@ -10,31 +10,18 @@ using namespace uuids;
TEST_CASE("Test default generator", "[gen][rand]")
{
uuid const guid = uuids::uuid_random_generator{}();
std::random_device rd;
auto seed_data = std::array<int, std::mt19937::state_size> {};
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
std::mt19937 generator(seq);
uuid const guid = uuids::uuid_random_generator{generator}();
REQUIRE(!guid.is_nil());
REQUIRE(guid.size() == 16);
REQUIRE(guid.version() == uuids::uuid_version::random_number_based);
REQUIRE(guid.variant() == uuids::uuid_variant::rfc);
}
TEST_CASE("Test random generator (default ctor)", "[gen][rand]")
{
uuids::uuid_random_generator dgen;
auto id1 = dgen();
REQUIRE(!id1.is_nil());
REQUIRE(id1.size() == 16);
REQUIRE(id1.version() == uuids::uuid_version::random_number_based);
REQUIRE(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen();
REQUIRE(!id2.is_nil());
REQUIRE(id2.size() == 16);
REQUIRE(id2.version() == uuids::uuid_version::random_number_based);
REQUIRE(id2.variant() == uuids::uuid_variant::rfc);
REQUIRE(id1 != id2);
}
TEST_CASE("Test random generator (conversion ctor w/ smart ptr)", "[gen][rand]")
{
std::random_device rd;
@ -46,13 +33,11 @@ TEST_CASE("Test random generator (conversion ctor w/ smart ptr)", "[gen][rand]")
uuids::uuid_random_generator dgen(&generator);
auto id1 = dgen();
REQUIRE(!id1.is_nil());
REQUIRE(id1.size() == 16);
REQUIRE(id1.version() == uuids::uuid_version::random_number_based);
REQUIRE(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen();
REQUIRE(!id2.is_nil());
REQUIRE(id2.size() == 16);
REQUIRE(id2.version() == uuids::uuid_version::random_number_based);
REQUIRE(id2.variant() == uuids::uuid_variant::rfc);
@ -70,13 +55,11 @@ TEST_CASE("Test random generator (conversion ctor w/ ptr)", "[gen][rand]")
uuids::uuid_random_generator dgen(generator.get());
auto id1 = dgen();
REQUIRE(!id1.is_nil());
REQUIRE(id1.size() == 16);
REQUIRE(id1.version() == uuids::uuid_version::random_number_based);
REQUIRE(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen();
REQUIRE(!id1.is_nil());
REQUIRE(id2.size() == 16);
REQUIRE(id2.version() == uuids::uuid_version::random_number_based);
REQUIRE(id2.variant() == uuids::uuid_variant::rfc);
@ -94,31 +77,11 @@ TEST_CASE("Test random generator (conversion ctor w/ ref)", "[gen][rand]")
uuids::uuid_random_generator dgen(generator);
auto id1 = dgen();
REQUIRE(!id1.is_nil());
REQUIRE(id1.size() == 16);
REQUIRE(id1.version() == uuids::uuid_version::random_number_based);
REQUIRE(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen();
REQUIRE(!id2.is_nil());
REQUIRE(id2.size() == 16);
REQUIRE(id2.version() == uuids::uuid_version::random_number_based);
REQUIRE(id2.variant() == uuids::uuid_variant::rfc);
REQUIRE(id1 != id2);
}
TEST_CASE("Test basic random generator (default ctor) w/ ranlux48_base", "[gen][rand]")
{
uuids::basic_uuid_random_generator<std::ranlux48_base> dgen;
auto id1 = dgen();
REQUIRE(!id1.is_nil());
REQUIRE(id1.size() == 16);
REQUIRE(id1.version() == uuids::uuid_version::random_number_based);
REQUIRE(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen();
REQUIRE(!id1.is_nil());
REQUIRE(id2.size() == 16);
REQUIRE(id2.version() == uuids::uuid_version::random_number_based);
REQUIRE(id2.variant() == uuids::uuid_variant::rfc);
@ -128,18 +91,19 @@ TEST_CASE("Test basic random generator (default ctor) w/ ranlux48_base", "[gen][
TEST_CASE("Test basic random generator (conversion ctor w/ ptr) w/ ranlux48_base", "[gen][rand]")
{
std::random_device rd;
std::ranlux48_base generator(rd());
auto seed_data = std::array<int, 6> {};
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
std::ranlux48_base generator(seq);
uuids::basic_uuid_random_generator<std::ranlux48_base> dgen(&generator);
auto id1 = dgen();
REQUIRE(!id1.is_nil());
REQUIRE(id1.size() == 16);
REQUIRE(id1.version() == uuids::uuid_version::random_number_based);
REQUIRE(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen();
REQUIRE(!id2.is_nil());
REQUIRE(id2.size() == 16);
REQUIRE(id2.version() == uuids::uuid_version::random_number_based);
REQUIRE(id2.variant() == uuids::uuid_variant::rfc);
@ -149,18 +113,19 @@ TEST_CASE("Test basic random generator (conversion ctor w/ ptr) w/ ranlux48_base
TEST_CASE("Test basic random generator (conversion ctor w/ smart ptr) w/ ranlux48_base", "[gen][rand]")
{
std::random_device rd;
auto generator = std::make_unique<std::ranlux48_base>(rd());
auto seed_data = std::array<int, 6> {};
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
auto generator = std::make_unique<std::ranlux48_base>(seq);
uuids::basic_uuid_random_generator<std::ranlux48_base> dgen(generator.get());
auto id1 = dgen();
REQUIRE(!id1.is_nil());
REQUIRE(id1.size() == 16);
REQUIRE(id1.version() == uuids::uuid_version::random_number_based);
REQUIRE(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen();
REQUIRE(!id2.is_nil());
REQUIRE(id2.size() == 16);
REQUIRE(id2.version() == uuids::uuid_version::random_number_based);
REQUIRE(id2.variant() == uuids::uuid_variant::rfc);
@ -170,18 +135,19 @@ TEST_CASE("Test basic random generator (conversion ctor w/ smart ptr) w/ ranlux4
TEST_CASE("Test basic random generator (conversion ctor w/ ref) w/ ranlux48_base", "[gen][rand]")
{
std::random_device rd;
std::ranlux48_base generator(rd());
auto seed_data = std::array<int, 6> {};
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
std::ranlux48_base generator(seq);
uuids::basic_uuid_random_generator<std::ranlux48_base> dgen(generator);
auto id1 = dgen();
REQUIRE(!id1.is_nil());
REQUIRE(id1.size() == 16);
REQUIRE(id1.version() == uuids::uuid_version::random_number_based);
REQUIRE(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen();
REQUIRE(!id2.is_nil());
REQUIRE(id2.size() == 16);
REQUIRE(id2.version() == uuids::uuid_version::random_number_based);
REQUIRE(id2.variant() == uuids::uuid_variant::rfc);
@ -190,28 +156,24 @@ TEST_CASE("Test basic random generator (conversion ctor w/ ref) w/ ranlux48_base
TEST_CASE("Test name generator", "[gen][name]")
{
uuids::uuid_name_generator dgen(uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43"));
uuids::uuid_name_generator dgen(uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value());
auto id1 = dgen("john");
REQUIRE(!id1.is_nil());
REQUIRE(id1.size() == 16);
REQUIRE(id1.version() == uuids::uuid_version::name_based_sha1);
REQUIRE(id1.variant() == uuids::uuid_variant::rfc);
auto id2 = dgen("jane");
REQUIRE(!id2.is_nil());
REQUIRE(id2.size() == 16);
REQUIRE(id2.version() == uuids::uuid_version::name_based_sha1);
REQUIRE(id2.variant() == uuids::uuid_variant::rfc);
auto id3 = dgen("jane");
REQUIRE(!id3.is_nil());
REQUIRE(id3.size() == 16);
REQUIRE(id3.version() == uuids::uuid_version::name_based_sha1);
REQUIRE(id3.variant() == uuids::uuid_variant::rfc);
auto id4 = dgen(L"jane");
REQUIRE(!id4.is_nil());
REQUIRE(id4.size() == 16);
REQUIRE(id4.version() == uuids::uuid_version::name_based_sha1);
REQUIRE(id4.variant() == uuids::uuid_variant::rfc);

View File

@ -9,82 +9,300 @@
using namespace uuids;
namespace
{
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0205r0.html
template <typename EngineT, std::size_t StateSize = EngineT::state_size>
void seed_rng(EngineT& engine)
{
using engine_type = typename EngineT::result_type;
using device_type = std::random_device::result_type;
using seedseq_type = std::seed_seq::result_type;
constexpr auto bytes_needed = StateSize * sizeof(engine_type);
constexpr auto numbers_needed = (sizeof(device_type) < sizeof(seedseq_type))
? (bytes_needed / sizeof(device_type))
: (bytes_needed / sizeof(seedseq_type));
std::array<device_type, numbers_needed> numbers{};
std::random_device rnddev{};
std::generate(std::begin(numbers), std::end(numbers), std::ref(rnddev));
std::seed_seq seedseq(std::cbegin(numbers), std::cend(numbers));
engine.seed(seedseq);
}
}
TEST_CASE("Test default constructor", "[ctors]")
{
uuid empty;
REQUIRE(empty.is_nil());
REQUIRE(empty.size() == 16);
}
TEST_CASE("Test from_string(string_view)", "[parse]")
TEST_CASE("Test string conversion", "[ops]")
{
uuid empty;
REQUIRE(uuids::to_string(empty) == "00000000-0000-0000-0000-000000000000");
REQUIRE(uuids::to_string<wchar_t>(empty) == L"00000000-0000-0000-0000-000000000000");
}
TEST_CASE("Test is_valid_uuid(char*)", "[parse]")
{
REQUIRE(uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43"));
REQUIRE(uuids::uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43}"));
REQUIRE(uuids::uuid::is_valid_uuid(L"47183823-2574-4bfd-b411-99ed177d3e43"));
REQUIRE(uuids::uuid::is_valid_uuid(L"{47183823-2574-4bfd-b411-99ed177d3e43}"));
REQUIRE(uuids::uuid::is_valid_uuid("00000000-0000-0000-0000-000000000000"));
REQUIRE(uuids::uuid::is_valid_uuid("{00000000-0000-0000-0000-000000000000}"));
REQUIRE(uuids::uuid::is_valid_uuid(L"00000000-0000-0000-0000-000000000000"));
REQUIRE(uuids::uuid::is_valid_uuid(L"{00000000-0000-0000-0000-000000000000}"));
}
TEST_CASE("Test is_valid_uuid(basic_string)", "[parse]")
{
using namespace std::string_literals;
{
auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s;
auto guid = uuids::uuid::from_string(str);
REQUIRE(uuids::to_string(guid) == str);
REQUIRE(uuids::uuid::is_valid_uuid(str));
}
{
auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"s;
auto guid = uuids::uuid::from_string(str);
REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
REQUIRE(uuids::uuid::is_valid_uuid(str));
}
{
auto guid = uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43");
REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
REQUIRE(uuids::to_wstring(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43");
}
{
auto str = "4718382325744bfdb41199ed177d3e43"s;
REQUIRE_NOTHROW(uuids::uuid::from_string(str));
}
}
TEST_CASE("Test from_string(wstring_view)", "[parse]")
{
using namespace std::string_literals;
auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s;
auto guid = uuids::uuid::from_string(str);
REQUIRE(uuids::to_wstring(guid) == str);
REQUIRE(uuids::uuid::is_valid_uuid(str));
}
TEST_CASE("Test from_string invalid format", "[parse]")
{
auto str = L"{47183823-2574-4bfd-b411-99ed177d3e43}"s;
REQUIRE(uuids::uuid::is_valid_uuid(str));
}
{
auto str = "00000000-0000-0000-0000-000000000000"s;
REQUIRE(uuids::uuid::is_valid_uuid(str));
}
{
auto str = "{00000000-0000-0000-0000-000000000000}"s;
REQUIRE(uuids::uuid::is_valid_uuid(str));
}
{
auto str = L"00000000-0000-0000-0000-000000000000"s;
REQUIRE(uuids::uuid::is_valid_uuid(str));
}
{
auto str = L"{00000000-0000-0000-0000-000000000000}"s;
REQUIRE(uuids::uuid::is_valid_uuid(str));
}
}
TEST_CASE("Test is_valid_uuid(char*) invalid format", "[parse]")
{
REQUIRE(!uuids::uuid::is_valid_uuid(""));
REQUIRE(!uuids::uuid::is_valid_uuid("{}"));
REQUIRE(!uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e4"));
REQUIRE(!uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e430"));
REQUIRE(!uuids::uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43"));
REQUIRE(!uuids::uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43}"));
}
TEST_CASE("Test is_valid_uuid(basic_string) invalid format", "[parse]")
{
using namespace std::string_literals;
{
auto str = ""s;
REQUIRE_THROWS_AS(uuids::uuid::from_string(str), uuids::uuid_error);
REQUIRE(!uuids::uuid::is_valid_uuid(str));
}
{
auto str = "{}"s;
REQUIRE_THROWS_AS(uuids::uuid::from_string(str), uuids::uuid_error);
REQUIRE(!uuids::uuid::is_valid_uuid(str));
}
{
auto str = "47183823-2574-4bfd-b411-99ed177d3e4"s;
REQUIRE_THROWS_AS(uuids::uuid::from_string(str), uuids::uuid_error);
REQUIRE(!uuids::uuid::is_valid_uuid(str));
}
{
auto str = "47183823-2574-4bfd-b411-99ed177d3e430"s;
REQUIRE_THROWS_AS(uuids::uuid::from_string(str), uuids::uuid_error);
REQUIRE(!uuids::uuid::is_valid_uuid(str));
}
{
auto str = "{47183823-2574-4bfd-b411-99ed177d3e43"s;
REQUIRE_THROWS_AS(uuids::uuid::from_string(str), uuids::uuid_error);
REQUIRE(!uuids::uuid::is_valid_uuid(str));
}
{
auto str = "47183823-2574-4bfd-b411-99ed177d3e43}"s;
REQUIRE_THROWS_AS(uuids::uuid::from_string(str), uuids::uuid_error);
REQUIRE(!uuids::uuid::is_valid_uuid(str));
}
}
TEST_CASE("Test from_string(char*)", "[parse]")
{
{
auto str = "47183823-2574-4bfd-b411-99ed177d3e43";
auto guid = uuids::uuid::from_string(str).value();
REQUIRE(uuids::to_string(guid) == str);
}
{
auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}";
auto guid = uuids::uuid::from_string(str).value();
REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
}
{
auto guid = uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value();
REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
REQUIRE(uuids::to_string<wchar_t>(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43");
}
{
auto str = L"47183823-2574-4bfd-b411-99ed177d3e43";
auto guid = uuids::uuid::from_string(str).value();
REQUIRE(uuids::to_string<wchar_t>(guid) == str);
}
{
auto str = "4718382325744bfdb41199ed177d3e43";
REQUIRE_NOTHROW(uuids::uuid::from_string(str));
REQUIRE(uuids::uuid::from_string(str).has_value());
}
{
auto str = "00000000-0000-0000-0000-000000000000";
auto guid = uuids::uuid::from_string(str).value();
REQUIRE(guid.is_nil());
}
{
auto str = "{00000000-0000-0000-0000-000000000000}";
auto guid = uuids::uuid::from_string(str).value();
REQUIRE(guid.is_nil());
}
{
auto str = L"00000000-0000-0000-0000-000000000000";
auto guid = uuids::uuid::from_string(str).value();
REQUIRE(guid.is_nil());
}
{
auto str = L"{00000000-0000-0000-0000-000000000000}";
auto guid = uuids::uuid::from_string(str).value();
REQUIRE(guid.is_nil());
}
}
TEST_CASE("Test from_string(basic_string)", "[parse]")
{
using namespace std::string_literals;
{
auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s;
auto guid = uuids::uuid::from_string(str).value();
REQUIRE(uuids::to_string(guid) == str);
}
{
auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"s;
auto guid = uuids::uuid::from_string(str).value();
REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
}
{
auto guid = uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value();
REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
REQUIRE(uuids::to_string<wchar_t>(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43");
}
{
auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s;
auto guid = uuids::uuid::from_string(str).value();
REQUIRE(uuids::to_string<wchar_t>(guid) == str);
}
{
auto str = "4718382325744bfdb41199ed177d3e43"s;
REQUIRE_NOTHROW(uuids::uuid::from_string(str));
REQUIRE(uuids::uuid::from_string(str).has_value());
}
{
auto str = "00000000-0000-0000-0000-000000000000"s;
auto guid = uuids::uuid::from_string(str).value();
REQUIRE(guid.is_nil());
}
{
auto str = "{00000000-0000-0000-0000-000000000000}"s;
auto guid = uuids::uuid::from_string(str).value();
REQUIRE(guid.is_nil());
}
{
auto str = L"00000000-0000-0000-0000-000000000000"s;
auto guid = uuids::uuid::from_string(str).value();
REQUIRE(guid.is_nil());
}
{
auto str = L"{00000000-0000-0000-0000-000000000000}"s;
auto guid = uuids::uuid::from_string(str).value();
REQUIRE(guid.is_nil());
}
}
TEST_CASE("Test from_string(char*) invalid format", "[parse]")
{
REQUIRE(!uuids::uuid::from_string("").has_value());
REQUIRE(!uuids::uuid::from_string("{}").has_value());
REQUIRE(!uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e4").has_value());
REQUIRE(!uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e430").has_value());
REQUIRE(!uuids::uuid::from_string("{47183823-2574-4bfd-b411-99ed177d3e43").has_value());
REQUIRE(!uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43}").has_value());
}
TEST_CASE("Test from_string(basic_string) invalid format", "[parse]")
{
using namespace std::string_literals;
{
auto str = ""s;
REQUIRE(!uuids::uuid::from_string(str).has_value());
}
{
auto str = "{}"s;
REQUIRE(!uuids::uuid::from_string(str).has_value());
}
{
auto str = "47183823-2574-4bfd-b411-99ed177d3e4"s;
REQUIRE(!uuids::uuid::from_string(str).has_value());
}
{
auto str = "47183823-2574-4bfd-b411-99ed177d3e430"s;
REQUIRE(!uuids::uuid::from_string(str).has_value());
}
{
auto str = "{47183823-2574-4bfd-b411-99ed177d3e43"s;
REQUIRE(!uuids::uuid::from_string(str).has_value());
}
{
auto str = "47183823-2574-4bfd-b411-99ed177d3e43}"s;
REQUIRE(!uuids::uuid::from_string(str).has_value());
}
}
@ -120,7 +338,10 @@ TEST_CASE("Test iterators constructor", "[ctors]")
TEST_CASE("Test equality", "[operators]")
{
uuid empty;
uuid guid = uuids::uuid_random_generator{}();
auto engine = uuids::uuid_random_generator::engine_type{};
seed_rng(engine);
uuid guid = uuids::uuid_random_generator{engine}();
REQUIRE(empty == empty);
REQUIRE(guid == guid);
@ -130,7 +351,11 @@ TEST_CASE("Test equality", "[operators]")
TEST_CASE("Test comparison", "[operators]")
{
auto empty = uuid{};
uuids::uuid_random_generator gen;
auto engine = uuids::uuid_random_generator::engine_type{};
seed_rng(engine);
uuids::uuid_random_generator gen{ engine };
auto id = gen();
REQUIRE(empty < id);
@ -151,13 +376,15 @@ TEST_CASE("Test hashing", "[ops]")
{
using namespace std::string_literals;
auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s;
auto guid = uuids::uuid::from_string(str);
auto guid = uuids::uuid::from_string(str).value();
auto h1 = std::hash<std::string>{};
auto h2 = std::hash<uuid>{};
REQUIRE(h1(str) == h2(guid));
uuids::uuid_random_generator gen;
auto engine = uuids::uuid_random_generator::engine_type{};
seed_rng(engine);
uuids::uuid_random_generator gen{ engine };
std::unordered_set<uuids::uuid> ids{
uuid{},
@ -174,7 +401,10 @@ TEST_CASE("Test hashing", "[ops]")
TEST_CASE("Test swap", "[ops]")
{
uuid empty;
uuid guid = uuids::uuid_random_generator{}();
auto engine = uuids::uuid_random_generator::engine_type{};
seed_rng(engine);
uuid guid = uuids::uuid_random_generator{engine}();
REQUIRE(empty.is_nil());
REQUIRE(!guid.is_nil());
@ -190,52 +420,10 @@ TEST_CASE("Test swap", "[ops]")
REQUIRE(!guid.is_nil());
}
TEST_CASE("Test string conversion", "[ops]")
{
uuid empty;
REQUIRE(uuids::to_string(empty) == "00000000-0000-0000-0000-000000000000");
REQUIRE(uuids::to_wstring(empty) == L"00000000-0000-0000-0000-000000000000");
}
TEST_CASE("Test iterators", "[iter]")
{
std::array<uuids::uuid::value_type, 16> arr{ {
0x47, 0x18, 0x38, 0x23,
0x25, 0x74,
0x4b, 0xfd,
0xb4, 0x11,
0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43
} };
{
uuid guid(arr);
REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
size_t i = 0;
for (auto const & b : guid)
{
REQUIRE(arr[i++] == b);
}
}
{
const uuid guid = uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43");
REQUIRE(!guid.is_nil());
REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
size_t i = 0;
for (auto const & b : guid)
{
REQUIRE(arr[i++] == b);
}
}
}
TEST_CASE("Test constexpr", "[const]")
{
constexpr uuid empty;
[[maybe_unused]] constexpr bool isnil = empty.is_nil();
[[maybe_unused]] constexpr size_t size = empty.size();
[[maybe_unused]] constexpr uuids::uuid_variant variant = empty.variant();
[[maybe_unused]] constexpr uuid_version version = empty.version();
}
@ -247,11 +435,11 @@ TEST_CASE("Test size", "[operators]")
TEST_CASE("Test assignment", "[ops]")
{
auto id1 = uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43");
auto id1 = uuids::uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value();
auto id2 = id1;
REQUIRE(id1 == id2);
id1 = uuids::uuid::from_string("{fea43102-064f-4444-adc2-02cec42623f8}");
id1 = uuids::uuid::from_string("{fea43102-064f-4444-adc2-02cec42623f8}").value();
REQUIRE(id1 != id2);
auto id3 = std::move(id2);