diff --git a/P0959.md b/P0959.md index 8060c65..8272a35 100644 --- a/P0959.md +++ b/P0959.md @@ -25,8 +25,9 @@ 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 `` 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`, `uuid_random_generator`, `uuid_name_generator` -* string conversion functions `std::to_string()`, `std::to_wstring()` as well as an overloaded `operator<<` for `std::basic_ostream` +* 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` * comparison operators `==`, `!=`, `<` * `std::swap()` overload for `uuid` * `std::hash<>` specialization for `uuid` @@ -40,22 +41,9 @@ uuid empty; auto empty = uuid{}; ``` -### string_view constructors - -Conversion constructors from `std::string_view` and `std::wstring_view` allow to create `uuid` instances from various strings. - -The input argument must have the form `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` where `x` is a hexadecimal digit. Should the argument be of a different format a nil UUID would be created. - -```cpp -uuid id1("47183823-2574-4bfd-b411-99ed177d3e43"); - -std::wstring str=L"47183823-2574-4bfd-b411-99ed177d3e43"; -uuid id2(str); -``` - ### Iterators constructors -The conversion constructor that takes two forward iterators constructs an `uuid` with the content of the range \[first, last). It requires the range to have exactly 16 elements, otherwise the result is a nil `uuid`. This constructor follows the conventions of other containers in the standard library. +The conversion constructor that takes two forward iterators constructs an `uuid` with the content of the range \[first, last). It requires the range to have exactly 16 elements, otherwise the behaviour is undefined. This constructor follows the conventions of other containers in the standard library. ```cpp std::array arr{{ @@ -78,6 +66,26 @@ uuid::value_type arr[16] = { uuid id(std::begin(arr), std::end(arr)); ``` +### Span constructor + +The conversion constructor that takes a `std::span` constructs a `uuid` from a contiguous sequence of 16 bytes. + +```cpp +std::array arr{ { + 0x47, 0x18, 0x38, 0x23, + 0x25, 0x74, + 0x4b, 0xfd, + 0xb4, 0x11, + 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 +} }; + +std::span data(arr); + +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. @@ -88,15 +96,19 @@ 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 the string conversion constructors when failing to parse the input argument. +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}`. + ```cpp uuid id; assert(id.is_nil()); + +uuid id = from_string("00000000-0000-0000-0000-000000000000"); +assert(id.is_nil()); ``` ### Iterators -Constant and mutable iterators allow direct access to the underlaying `uuid` data. This enables both direct reading and writing of the `uuid` bits. The `uuid` class has both const and non-const `begin()` and `end()` members to return constant and mutable iterators to the first and the one-past-last element of the UUID data. +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 arr{{ @@ -107,12 +119,8 @@ std::array arr{{ 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 }}; -uuid id; -assert(id.is_nil()); - -std::copy(std::cbegin(arr), std::cend(arr), std::begin(id)); -assert(!id.is_nil()); -assert(id.string() == "47183823-2574-4bfd-b411-99ed177d3e43"); +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) @@ -131,6 +139,24 @@ assert(id.version() == uuid_version::random_number_based); assert(id.variant() == uuid_variant::rfc); ``` +### Span view +Member function `as_bytes()` converts the `uuid` into a view of its underlying bytes. +```cpp +std::array arr{ { + 0x47, 0x18, 0x38, 0x23, + 0x25, 0x74, + 0x4b, 0xfd, + 0xb4, 0x11, + 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 + } }; + +const uuid id{ arr }; +assert(!id.is_nil()); + +auto view = id.as_bytes(); +assert(memcmp(view.data(), arr.data(), arr.size()) == 0); +``` + ### Swapping Both member and non-member `swap()` functions are available to perform the swapping of `uuid` values. @@ -153,6 +179,17 @@ assert(empty.is_nil()); assert(!id.is_nil()); ``` +### string parsing + +Non-member overloaded function `from_string()` allow 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. + +```cpp +auto id1 = from_string("47183823-2574-4bfd-b411-99ed177d3e43"); +auto id2 = from_string(L"{47183823-2574-4bfd-b411-99ed177d3e43}"); +``` + ### 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. @@ -324,52 +361,54 @@ 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 namespace std { -struct uuid -{ -public: - typedef uint8_t value_type; - typedef uint8_t& reference; - typedef uint8_t const& const_reference; - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; + struct uuid + { + typedef uint8_t value_type; + typedef /*implementation-defined*/ const_iterator; + + constexpr uuid() noexcept = default; + + explicit uuid(std::span bytes); - typedef /*implementation-defined*/ iterator; - typedef /*implementation-defined*/ const_iterator; - - static constexpr size_t state_size = 16; + template + explicit uuid(ForwardIterator first, ForwardIterator last); -public: - constexpr uuid() noexcept; + 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; - template - explicit uuid(ForwardIterator first, ForwardIterator last); + void swap(uuid & other) noexcept; - explicit uuid(std::string_view str); - explicit uuid(std::wstring_view str); + const_iterator begin() const noexcept; + const_iterator end() const noexcept; - 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; + std::span as_bytes(); + std::span as_bytes() const; + + private: + friend bool operator==(uuid const & lhs, uuid const & rhs) noexcept; + friend bool operator<(uuid const & lhs, uuid const & rhs) noexcept; - void swap(uuid & other) noexcept; - - iterator begin() noexcept; - const_iterator begin() const noexcept; - iterator end() noexcept; - const_iterator end() const noexcept; - -private: - friend bool operator==(uuid const & lhs, uuid const & rhs) noexcept; - friend bool operator<(uuid const & lhs, uuid const & rhs) noexcept; - - template - friend std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id); -}; + template + friend std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id); + }; +} ``` ### non-member functions @@ -387,41 +426,54 @@ namespace std { inline std::string to_string(uuid const & id); inline std::wstring to_wstring(uuid const & id); + + template + inline uuid from_string(TChar const * const str, size_t const size); + inline uuid from_string(std::string_view str); + inline uuid from_string(std::wstring_view str); + + inline std::span as_bytes(uuid id); } ``` ### Generators `basic_uuid_random_generator` is a class template for generating random or pseudo-random UUIDs (version 4, i.e. `uuid_version::random_number_based`). The type template parameter represents a function object that implements both the [`RandomNumberEngine`](http://en.cppreference.com/w/cpp/concept/UniformRandomBitGenerator) and [`UniformRandomBitGenerator`](http://en.cppreference.com/w/cpp/concept/RandomNumberEngine) concepts. `basic_uuid_random_generator` can be either default constructed or constructed with a reference or pointer to a an objects that satisfies the `UniformRandomNumberGenerator` requirements. ```cpp -template -class basic_uuid_random_generator -{ -public: - typedef uuid result_type; +namespace std { + template + class basic_uuid_random_generator + { + public: + typedef uuid result_type; - basic_uuid_random_generator(); - explicit basic_uuid_random_generator(UniformRandomNumberGenerator& gen); - explicit basic_uuid_random_generator(UniformRandomNumberGenerator* gen); + basic_uuid_random_generator(); + explicit basic_uuid_random_generator(UniformRandomNumberGenerator& gen); + explicit basic_uuid_random_generator(UniformRandomNumberGenerator* gen); - uuid operator()(); -}; + uuid operator()(); + }; +} ``` A type alias `uuid_random_generator` is provided for convenience as `std::mt19937` is probably the preferred choice of a pseudo-random number generator engine in most cases. ```cpp -using uuid_random_generator = basic_uuid_random_generator; +namespace std { + using uuid_random_generator = basic_uuid_random_generator; +} ``` `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`. ```cpp -class uuid_name_generator -{ -public: - typedef uuid result_type; +namespace std { + class uuid_name_generator + { + public: + typedef uuid result_type; - explicit uuid_name_generator(uuid const& namespace_uuid) noexcept; - - uuid operator()(std::string_view name); - uuid operator()(std::wstring_view name); -}; + explicit uuid_name_generator(uuid const& namespace_uuid) noexcept; + + uuid operator()(std::string_view name); + uuid operator()(std::wstring_view name); + }; +} ``` ### Specialization diff --git a/README.md b/README.md index eec2215..452122a 100644 --- a/README.md +++ b/README.md @@ -44,67 +44,96 @@ Other: This project is currently under development and should be ignored until further notice. +## Library history +This library is an implementation of the proposal P0959. As the proposal evolves based on the standard commity and the C++ community feedback, this library implementation will reflect those changes. + +### Revision 1 +Changes from the first draft: +* Removed string constructors and replaced with free overloaded function `from_string()`. +* Parsing strings to `uuid` throws exception `uuid_error` instead of creating a nil uuid when the operation fails. +* {} included in the supported format, i.e. `{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}`. +* Removed `state_size`. +* Rename member function `nil()` to `is_nil()`. +* The default constructor is defaulted. +* Added a conversion construct from `std::span`. +* Added the free 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. +* Removed mutable iterators (but preserved the constant iterators). +* Removed typedefs and others container-like parts. + ## Using the library The following is a list of examples for using the library: * Creating a nil UUID + ```cpp uuid empty; assert(empty.is_nil()); assert(empty.size() == 16); ``` + * Creating a new UUID + ```cpp -uuid const guid = uuids::uuid_system_generator{}(); -assert(!guid.is_nil()); -assert(guid.size() == 16); -assert(guid.version() == uuids::uuid_version::random_number_based); -assert(guid.variant() == uuids::uuid_variant::rfc); +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); ``` + * Creating a new UUID with a default random generator + ```cpp uuids::uuid_random_generator gen; -uuid const guid = gen(); -assert(!guid.is_nil()); -assert(guid.size() == 16); -assert(guid.version() == uuids::uuid_version::random_number_based); -assert(guid.variant() == uuids::uuid_variant::rfc); +uuid const id = gen(); +assert(!id.is_nil()); +assert(id.size() == 16); +assert(id.version() == uuids::uuid_version::random_number_based); +assert(id.variant() == uuids::uuid_variant::rfc); ``` + * Creating a new UUID with a particular random generator + ```cpp std::random_device rd; std::ranlux48_base generator(rd()); uuids::basic_uuid_random_generator gen(&generator); - -uuid const guid = gen(); -assert(!guid.is_nil()); -assert(guid.size() == 16); -assert(guid.version() == uuids::uuid_version::random_number_based); -assert(guid.variant() == uuids::uuid_variant::rfc); +uuid const id = gen(); +assert(!id.is_nil()); +assert(id.size() == 16); +assert(id.version() == uuids::uuid_version::random_number_based); +assert(id.variant() == uuids::uuid_variant::rfc); ``` + * Creating a new UUID with the name generator + ```cpp uuids::uuid_name_generator gen; -uuid const guid = gen(); -assert(!guid.is_nil()); -assert(guid.size() == 16); -assert(guid.version() == uuids::uuid_version::name_based_sha1); -assert(guid.variant() == uuids::uuid_variant::rfc); +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); ``` + * Create a UUID from a string + ```cpp using namespace std::string_literals; auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; -uuid guid(str); -assert(guid.string() == str); +uuid id(from_string(str)); +assert(uuids::to_string(id) == str); + +// or + +uuid id(from_string(L"{47183823-2574-4bfd-b411-99ed177d3e43}"s)); +assert(id.wstring() == str); ``` -or -```cpp -auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s; -uuid guid(str); -assert(guid.wstring() == str); -``` -* Creating a UUID from an array + +* Creating a UUID from sequence of 16 bytes + ```cpp std::array arr{{ 0x47, 0x18, 0x38, 0x23, @@ -112,54 +141,60 @@ std::array arr{{ 0x4b, 0xfd, 0xb4, 0x11, 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43}}; -uuid guid(std::begin(arr), std::end(arr)); -assert(id.string() == "47183823-2574-4bfd-b411-99ed177d3e43"); -``` -or -```cpp +uuid id(arr); + +assert(uuids::to_string(id) == "47183823-2574-4bfd-b411-99ed177d3e43"); + +// or + uuids::uuid::value_type arr[16] = { 0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd, 0xb4, 0x11, 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 }; -uuid guid(std::begin(arr), std::end(arr)); -assert(guid.string() == "47183823-2574-4bfd-b411-99ed177d3e43"); +uuid id(std::begin(arr), std::end(arr)); +assert(uuids::to_string(id) == "47183823-2574-4bfd-b411-99ed177d3e43"); ``` * Comparing UUIDs -```cpp -uuid empty; -uuid guid = uuids::uuid_system_generator{}(); -assert(empty == empty); -assert(guid == guid); -assert(empty != guid); -``` -* Swapping UUIDs ```cpp uuid empty; -uuid guid = uuids::uuid_system_generator{}(); +uuid id = uuids::uuid_system_generator{}(); +assert(empty == empty); +assert(id == id); +assert(empty != id); +``` + +* Swapping UUIDs + +```cpp +uuid empty; +uuid id = uuids::uuid_system_generator{}(); assert(empty.is_nil()); -assert(!guid.is_nil()); +assert(!id.is_nil()); -std::swap(empty, guid); +std::swap(empty, id); assert(!empty.is_nil()); -assert(guid.is_nil()); +assert(id.is_nil()); -empty.swap(guid); +empty.swap(id); assert(empty.is_nil()); -assert(!guid.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 arr{{ 0x47, 0x18, 0x38, 0x23, @@ -168,18 +203,15 @@ std::array arr{{ 0xb4, 0x11, 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43}}; -uuid guid; -assert(guid.is_nil()); - -std::copy(std::cbegin(arr), std::cend(arr), std::begin(guid)); -assert(!guid.is_nil()); -assert(guid.string() == "47183823-2574-4bfd-b411-99ed177d3e43"); +uuid id(arr); size_t i = 0; -for (auto const & b : guid) +for (auto const & b : id) assert(arr[i++] == b); ``` + * Using with an orderered associative container + ```cpp uuids::uuid_random_generator gen; std::set ids{uuid{}, gen(), gen(), gen(), gen()}; @@ -187,7 +219,9 @@ std::set ids{uuid{}, gen(), gen(), gen(), gen()}; assert(ids.size() == 5); assert(ids.find(uuid{}) != ids.end()); ``` + * Using in an unordered associative container + ```cpp uuids::uuid_random_generator gen; std::unordered_set ids{uuid{}, gen(), gen(), gen(), gen()}; @@ -195,16 +229,25 @@ std::unordered_set ids{uuid{}, gen(), gen(), gen(), gen()}; assert(ids.size() == 5); assert(ids.find(uuid{}) != ids.end()); ``` + * Hashing UUIDs + ```cpp +using namespace std::string_literals; +auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; +uuid id(from_string(str)); + auto h1 = std::hash{}; auto h2 = std::hash{}; -assert(h1(str) == h2(guid)); +assert(h1(str) == h2(id)); ``` ## 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). + ## Testing A testing project is available in the sources. To build and execute the tests do the following: * Clone or download this repository diff --git a/include/uuid.h b/include/uuid.h index f5c6559..2ef81d1 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -324,7 +325,7 @@ namespace uuids self_type & operator++ () { - if (index >= uuid::state_size) + if (index >= 16) throw std::out_of_range("Iterator cannot be incremented past the end of the data."); ++index; return *this; @@ -420,8 +421,8 @@ namespace uuids self_type & operator+=(difference_type const offset) { - if (static_cast(index) + offset < 0 || - static_cast(index) + offset > uuid::state_size) + if (static_cast(index) + offset < 0 || + static_cast(index) + offset > 16) throw std::out_of_range("Iterator cannot be incremented outside data bounds."); index += offset; @@ -439,187 +440,23 @@ namespace uuids } }; - struct uuid_iterator : public uuid_const_iterator + typedef uint8_t value_type; + + public: + constexpr uuid() noexcept = default; + + explicit uuid(gsl::span bytes) { - typedef uuid_iterator self_type; - typedef uint8_t value_type; - typedef uint8_t& reference; - typedef uint8_t* pointer; - typedef std::random_access_iterator_tag iterator_category; - typedef ptrdiff_t difference_type; - - protected: - pointer ptr = nullptr; - size_t index = 0; - - bool compatible(self_type const & other) const noexcept - { - return ptr == other.ptr; - } - - public: - explicit uuid_iterator(pointer ptr, size_t const index) : - ptr(ptr), index(index) - { - } - - uuid_iterator(uuid_iterator const & o) = default; - uuid_iterator& operator=(uuid_iterator const & o) = default; - ~uuid_iterator() = default; - - self_type & operator++ () - { - if (index >= uuid::state_size) - 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_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(index) + offset < 0 || - static_cast(index) + offset > uuid::state_size) - 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 & operator[](difference_type const offset) - { - return (*(*this + offset)); - } - - value_type const & operator[](difference_type const offset) const - { - return (*(*this + offset)); - } - }; - - public: - typedef uint8_t value_type; - typedef uint8_t& reference; - typedef uint8_t const& const_reference; - typedef uuid_iterator iterator; - typedef uuid_const_iterator const_iterator; - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; - - static constexpr size_t state_size = 16; - - public: - constexpr uuid() noexcept {} - + std::copy(std::cbegin(bytes), std::cend(bytes), std::begin(data)); + } + template explicit uuid(ForwardIterator first, ForwardIterator last) { if (std::distance(first, last) == 16) std::copy(first, last, std::begin(data)); } - - explicit uuid(std::string_view str) - { - create(str.data(), str.size()); - } - - explicit uuid(std::wstring_view str) - { - create(str.data(), str.size()); - } - + constexpr uuid_variant variant() const noexcept { if ((data[8] & 0x80) == 0x00) @@ -648,7 +485,7 @@ namespace uuids return uuid_version::none; } - constexpr std::size_t size() const noexcept { return state_size; } + constexpr std::size_t size() const noexcept { return 16; } constexpr bool is_nil() const noexcept { @@ -661,53 +498,40 @@ namespace uuids data.swap(other.data); } - iterator begin() noexcept { return uuid_iterator(&data[0], 0); } - const_iterator begin() const noexcept { return uuid_const_iterator(&data[0], 0); } - iterator end() noexcept { return uuid_iterator(&data[0], state_size); } - const_iterator end() const noexcept { return uuid_const_iterator(&data[0], state_size); } + uuid_const_iterator begin() const noexcept { return uuid_const_iterator(&data[0], 0); } + uuid_const_iterator end() const noexcept { return uuid_const_iterator(&data[0], 16); } + inline gsl::span as_bytes() + { + return gsl::span(reinterpret_cast(data.data()), 16); + } + + inline gsl::span as_bytes() const + { + return gsl::span(reinterpret_cast(data.data()), 16); + } private: - std::array data{ { 0 } }; + std::array data{ { 0 } }; friend bool operator==(uuid const & lhs, uuid const & rhs) noexcept; friend bool operator<(uuid const & lhs, uuid const & rhs) noexcept; template - friend std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id); - - template - void create(TChar const * const str, size_t const size) + friend std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id); + + //friend gsl::span as_bytes(uuid id); + }; + + struct uuid_error : public std::runtime_error + { + explicit uuid_error(std::string_view message) + : std::runtime_error(message.data()) { - TChar digit = 0; - bool firstdigit = true; - size_t index = 0; + } - for (size_t i = 0; i < size; ++i) - { - if (str[i] == static_cast('-')) continue; - - if (index >= 16 || !detail::is_hex(str[i])) - { - std::fill(std::begin(data), std::end(data), 0); - return; - } - - if (firstdigit) - { - digit = str[i]; - firstdigit = false; - } - else - { - data[index++] = detail::hexpair2char(digit, str[i]); - firstdigit = true; - } - } - - if (index < 16) - { - std::fill(std::begin(data), std::end(data), 0); - } + explicit uuid_error(char const * message) + : std::runtime_error(message) + { } }; @@ -752,6 +576,62 @@ namespace uuids << std::setw(2) << (int)id.data[15]; } + template + inline uuid from_string(TChar const * const str, size_t const size) + { + TChar digit = 0; + bool firstDigit = true; + int hasBraces = 0; + size_t index = 0; + std::array data{ { 0 } }; + + if(str == nullptr || size == 0) + throw uuid_error{ "Wrong uuid format" }; + + if (str[0] == static_cast('{')) + hasBraces = 1; + if(hasBraces && str[size-1] != static_cast('}')) + throw uuid_error{ "Wrong uuid format" }; + + for (size_t i = hasBraces; i < size - hasBraces; ++i) + { + if (str[i] == static_cast('-')) continue; + + if (index >= 16 || !detail::is_hex(str[i])) + { + throw uuid_error{"Wrong uuid format"}; + } + + if (firstDigit) + { + digit = str[i]; + firstDigit = false; + } + else + { + data[index++] = detail::hexpair2char(digit, str[i]); + firstDigit = true; + } + } + + if (index < 16) + { + throw uuid_error{"Wrong uuid format"}; + } + + return uuid{std::cbegin(data), std::cend(data)}; + } + + inline uuid from_string(std::string_view str) + { + return from_string(str.data(), str.size()); + } + + inline uuid from_string(std::wstring_view str) + { + return from_string(str.data(), str.size()); + } + inline std::string to_string(uuid const & id) { std::stringstream sstr; @@ -935,9 +815,9 @@ namespace uuids void reset() { hasher.reset(); - uint8_t bytes[uuid::state_size]; + uint8_t bytes[16]; std::copy(std::begin(nsuuid), std::end(nsuuid), bytes); - hasher.process_bytes(bytes, uuid::state_size); + hasher.process_bytes(bytes, 16); } template #include #include +#include using namespace uuids; @@ -14,32 +15,78 @@ TEST_CASE("Test default constructor", "[ctors]") REQUIRE(empty.size() == 16); } -TEST_CASE("Test string_view constructor", "[ctors]") +TEST_CASE("Test from_string(string_view)", "[parse]") { using namespace std::string_literals; { auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; - uuid guid(str); + auto guid = uuids::from_string(str); REQUIRE(uuids::to_string(guid) == str); } { - uuid guid("47183823-2574-4bfd-b411-99ed177d3e43"); + auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"s; + auto guid = uuids::from_string(str); + REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); + } + + { + auto guid = uuids::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::from_string(str)); + } } -TEST_CASE("Test wstring_view constructor", "[ctors]") +TEST_CASE("Test from_string(wstring_view)", "[parse]") { using namespace std::string_literals; auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s; - uuid guid(str); + auto guid = uuids::from_string(str); REQUIRE(uuids::to_wstring(guid) == str); } +TEST_CASE("Test from_string invalid format", "[parse]") +{ + using namespace std::string_literals; + + { + auto str = ""s; + REQUIRE_THROWS_AS(uuids::from_string(str), uuids::uuid_error); + } + + { + auto str = "{}"s; + REQUIRE_THROWS_AS(uuids::from_string(str), uuids::uuid_error); + } + + { + auto str = "47183823-2574-4bfd-b411-99ed177d3e4"s; + REQUIRE_THROWS_AS(uuids::from_string(str), uuids::uuid_error); + } + + { + auto str = "47183823-2574-4bfd-b411-99ed177d3e430"s; + REQUIRE_THROWS_AS(uuids::from_string(str), uuids::uuid_error); + } + + { + auto str = "{47183823-2574-4bfd-b411-99ed177d3e43"s; + REQUIRE_THROWS_AS(uuids::from_string(str), uuids::uuid_error); + } + + { + auto str = "47183823-2574-4bfd-b411-99ed177d3e43}"s; + REQUIRE_THROWS_AS(uuids::from_string(str), uuids::uuid_error); + } +} + TEST_CASE("Test iterators constructor", "[ctors]") { using namespace std::string_literals; @@ -103,7 +150,7 @@ TEST_CASE("Test hashing", "[ops]") { using namespace std::string_literals; auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; - auto guid = uuid{ str }; + auto guid = uuids::from_string(str); auto h1 = std::hash{}; auto h2 = std::hash{}; @@ -160,11 +207,7 @@ TEST_CASE("Test iterators", "[iter]") } }; { - uuid guid; - REQUIRE(guid.is_nil()); - - std::copy(std::cbegin(arr), std::cend(arr), std::begin(guid)); - REQUIRE(!guid.is_nil()); + uuid guid(arr); REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); size_t i = 0; @@ -175,7 +218,7 @@ TEST_CASE("Test iterators", "[iter]") } { - const uuid guid("47183823-2574-4bfd-b411-99ed177d3e43"); + const uuid guid = uuids::from_string("47183823-2574-4bfd-b411-99ed177d3e43"); REQUIRE(!guid.is_nil()); REQUIRE(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); @@ -194,4 +237,55 @@ TEST_CASE("Test constexpr", "[const]") constexpr size_t size = empty.size(); constexpr uuid_variant variant = empty.variant(); constexpr uuid_version version = empty.version(); +} + +TEST_CASE("Test size", "[operators]") +{ + REQUIRE(sizeof(uuid) == 16); +} + +TEST_CASE("Test assignment", "[ops]") +{ + auto id1 = uuids::from_string("47183823-2574-4bfd-b411-99ed177d3e43"); + auto id2 = id1; + REQUIRE(id1 == id2); + + id1 = uuids::from_string("{fea43102-064f-4444-adc2-02cec42623f8}"); + REQUIRE(id1 != id2); + + auto id3 = std::move(id2); + REQUIRE(uuids::to_string(id3) == "47183823-2574-4bfd-b411-99ed177d3e43"); +} + +TEST_CASE("Test trivial", "[trivial]") +{ + REQUIRE(std::is_trivially_copyable_v); +} + +TEST_CASE("Test as_bytes", "[ops]") +{ + std::array arr{ { + 0x47, 0x18, 0x38, 0x23, + 0x25, 0x74, + 0x4b, 0xfd, + 0xb4, 0x11, + 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 + } }; + + { + uuids::uuid id{ arr }; + REQUIRE(!id.is_nil()); + + auto view = id.as_bytes(); + REQUIRE(memcmp(view.data(), arr.data(), arr.size()) == 0); + } + + { + const uuids::uuid id{ arr }; + REQUIRE(!id.is_nil()); + + auto view = id.as_bytes(); + REQUIRE(memcmp(view.data(), arr.data(), arr.size()) == 0); + } + } \ No newline at end of file