diff --git a/doc/uuid/namespaces.adoc b/doc/uuid/namespaces.adoc index 43518ce..8f773eb 100644 --- a/doc/uuid/namespaces.adoc +++ b/doc/uuid/namespaces.adoc @@ -16,7 +16,7 @@ uuid url() noexcept; uuid oid() noexcept; uuid x500dn() noexcept; -}}} //namespace boost::uuids::ns +}}} // namespace boost::uuids::ns ---- === Namespaces diff --git a/doc/uuid/reference.adoc b/doc/uuid/reference.adoc index 5e668dd..6e1896c 100644 --- a/doc/uuid/reference.adoc +++ b/doc/uuid/reference.adoc @@ -3,6 +3,7 @@ include::uuid_all.adoc[] include::uuid.adoc[] +include::uuid_io.adoc[] include::uuid_generators.adoc[] include::nil_generator.adoc[] include::string_generator.adoc[] @@ -12,4 +13,8 @@ include::name_generator_md5.adoc[] include::name_generator.adoc[] include::basic_random_generator.adoc[] include::random_generator.adoc[] -include::uuid_io.adoc[] +include::uuid_clock.adoc[] +include::time_generator_v1.adoc[] +include::time_generator_v6.adoc[] +include::time_generator_v7.adoc[] +include::time_generator.adoc[] diff --git a/doc/uuid/time_generator.adoc b/doc/uuid/time_generator.adoc new file mode 100644 index 0000000..ab4fb26 --- /dev/null +++ b/doc/uuid/time_generator.adoc @@ -0,0 +1,15 @@ +[#time_generator] +== + +:idprefix: time_generator_ + +=== Synopsis + +[source,c++] +---- +#include +#include +#include +---- + +This convenience header makes the three time-based generators `time_generator_v1`, `time_generator_v6`, and `time_generator_v7`, available. diff --git a/doc/uuid/time_generator_v1.adoc b/doc/uuid/time_generator_v1.adoc new file mode 100644 index 0000000..1127e57 --- /dev/null +++ b/doc/uuid/time_generator_v1.adoc @@ -0,0 +1,96 @@ +[#time_generator_v1] +== + +:idprefix: time_generator_v1_ + +=== Synopsis + +[source,c++] +---- +namespace boost { +namespace uuids { + +class time_generator_v1 +{ +public: + + struct state_type + { + std::uint64_t timestamp; + std::uint16_t clock_seq; + }; + +private: // exposition only + + uuid::node_type node_ = {}; + + std::atomic* ps_ = nullptr; + state_type state_ = {}; + +public: + + using result_type = uuid; + + time_generator_v1(); + time_generator_v1( uuid::node_type const& node, state_type const& state ) noexcept; + time_generator_v1( uuid::node_type const& node, std::atomic& state ) noexcept; + + result_type operator()() noexcept; +}; + +}} // namespace boost::uuids +---- + +The class `time_generator_v1` generates time-based version 1 UUIDs. +It supports three modes of operation. + +The default and recommended one is by using its default constructor. +This instructs the generator to use a pseudorandom node identifier and initial clock sequence. + +If more control over the node identifier (or the clock sequence) is desired, +for example, +if the generated UUIDs must use a specific node identifier that persists for the lifetime of the program or even across different program runs, +a constructor that takes a node identifier and a `state_type` is provided. +(The `timestamp` field of the provided `state_type` should generally be zero.) + +Finally, if the program has several `time_generator_v1` instances (for example, one per thread) that need to use the same node identifier, +the third constructor takes a node identifier and a reference to `std::atomic`. +The atomic state is used by the generators to ensure that no duplicate UUIDs are produced. + +=== Constructors + +``` +time_generator_v1(); +``` + +Effects: :: Using entropy from `std::random_device`, generates a pseudorandom 48 bit node identifier in `node_` and a pseudorandom 14 bit clock sequence in `state_.clock_seq`. Initalizes `state_.timestamp` to zero. + +Remarks: :: The multicast bit of `node_` is set to denote a local node identifier, as recommended in https://www.rfc-editor.org/rfc/rfc4122.html#section-4.5[RFC 4122 Section 4.5]. + +``` +time_generator_v1( uuid::node_type const& node, state_type const& state ) noexcept; +``` + +Effects: :: Initializes `node_` to `node` and `state_` to `state`. + +``` +time_generator_v1( uuid::node_type const& node, std::atomic& state ) noexcept; +``` + +Effects: :: Initializes `node_` to `node` and `ps_` to `&state`. + +=== operator() + +``` +result_type operator()() noexcept; +``` + +Effects: :: ++ +Using the state in `state_`, if `ps_` is `nullptr`, or the state in `*ps_`, if `ps_` is not `nullptr`, +atomically computes and sets a new state by retrieving the system time as if by `uuid_clock::now()`, +converting it to a timestamp as if by `uuid_clock::to_timestamp`, +storing the result in `state.timestamp`, +and incrementing `state.clock_seq` modulo 0x4000 if the new timestamp is lower than or equal to the previous timestamp. ++ +Creates a version 1 UUID using the node identifier from `node_` and the new timestamp and clock sequence and returns it. diff --git a/doc/uuid/time_generator_v6.adoc b/doc/uuid/time_generator_v6.adoc new file mode 100644 index 0000000..ab0e44e --- /dev/null +++ b/doc/uuid/time_generator_v6.adoc @@ -0,0 +1,46 @@ +[#time_generator_v6] +== + +:idprefix: time_generator_v6_ + +=== Synopsis + +[source,c++] +---- +namespace boost { +namespace uuids { + +class time_generator_v6 +{ +public: + + struct state_type + { + std::uint64_t timestamp; + std::uint16_t clock_seq; + }; + +private: // exposition only + + uuid::node_type node_ = {}; + + std::atomic* ps_ = nullptr; + state_type state_ = {}; + +public: + + using result_type = uuid; + + time_generator_v6(); + time_generator_v6( uuid::node_type const& node, state_type const& state ) noexcept; + time_generator_v6( uuid::node_type const& node, std::atomic& state ) noexcept; + + result_type operator()() noexcept; +}; + +}} // namespace boost::uuids +---- + +The class `time_generator_v6` generates time-based version 6 UUIDs, as described in https://datatracker.ietf.org/doc/draft-ietf-uuidrev-rfc4122bis/[rfc4122bis] section 5.6. + +It operates in the exact same manner as `time_generator_v1`, with the only difference being that it produces version 6 UUIDs rather than version 1 ones. diff --git a/doc/uuid/time_generator_v7.adoc b/doc/uuid/time_generator_v7.adoc new file mode 100644 index 0000000..25105c8 --- /dev/null +++ b/doc/uuid/time_generator_v7.adoc @@ -0,0 +1,62 @@ +[#time_generator_v7] +== + +:idprefix: time_generator_v7_ + +=== Synopsis + +[source,c++] +[subs=+quotes] +---- +namespace boost { +namespace uuids { + +class time_generator_v7 +{ +private: + + // exposition only + _unspecified-csprng-type_ rng_; + +public: + + using result_type = uuid; + + time_generator_v7(); + + result_type operator()() noexcept; +}; + +}} // namespace boost::uuids +---- + +The class `time_generator_v7` generates time-based version 7 UUIDs, as described in https://datatracker.ietf.org/doc/draft-ietf-uuidrev-rfc4122bis/[rfc4122bis] section 5.7. + +=== Constructor + +``` +time_generator_v7(); +``` + +Effects: :: Initializes the internal cryptographically strong pseudorandom number generator (CSPRNG) `rng_` using entropy from `std::random_device`. + +=== operator() + +``` +result_type operator()() noexcept; +``` + +Effects: :: ++ +. Obtains a new timestamp as if by getting the system time from `std::chrono::system_clock::now()` and converting it to a number of microseconds from the Unix epoch. +. Creates a new version 7 UUID and initializes its `unix_ts_ms` field with the number of milliseconds in the timestamp. +. Initializes the `rand_a` field with the residual number of microseconds from the timestamp. +. Initializes the 6 bits of the `rand_b` field following the variant to a conflict resolution counter, such that if more than one UUID is generated using the same timestamp, monotonicity is still ensured. +. Initializes the rest of the `rand_b` field to random values obtained from the internal CSPRNG `rng_`. +. Returns the UUID. + +Remarks: :: `operator()` generates a monotonically increasing sequence of UUIDs, if the following requirements are met: ++ +* The system clock never goes backwards; +* The system clock has at least millisecond resolution; +* No more than 64 UUIDs are generated per microsecond (no more than one every 16 nanoseconds.) diff --git a/doc/uuid/uuid_clock.adoc b/doc/uuid/uuid_clock.adoc new file mode 100644 index 0000000..bee940a --- /dev/null +++ b/doc/uuid/uuid_clock.adoc @@ -0,0 +1,110 @@ +[#uuid_clock] +== + +:idprefix: uuid_clock_ + +=== Synopsis + +[source,c++] +---- +namespace boost { +namespace uuids { + +class uuid_clock +{ +public: + + using rep = std::int64_t; + using period = std::ratio<1, 10000000>; // 100ns + using duration = std::chrono::duration; + using time_point = std::chrono::time_point; + + static constexpr bool is_steady = false; + + static time_point now() noexcept; + + static time_point from_sys( std::chrono::system_clock::time_point const& tp ) noexcept; + static std::chrono::system_clock::time_point to_sys( time_point const& tp ) noexcept; + + static time_point from_timestamp( std::uint64_t timestamp ) noexcept; + static std::uint64_t to_timestamp( time_point const& tp ) noexcept; +}; + + +}} // namespace boost::uuids +---- + +The class `uuid_clock` is a ``-compatible clock with an epoch of 00:00:00.00, 15 October 1582, and a resolution of 100 ns. +These values are specified in https://www.rfc-editor.org/rfc/rfc4122.html#section-4.1.4[RFC 4122 Section 4.1.4]. + +=== now + +``` +static time_point now() noexcept; +``` + +Returns: :: The current system time (`std::chrono::system_clock::now()`, converted to `uuid_clock::time_point`.) + +=== from_sys + +``` +static time_point from_sys( std::chrono::system_clock::time_point const& tp ) noexcept; +``` + +Returns: :: The `uuid_clock::time_point` corresponding to `tp`. + +=== to_sys + +``` +static std::chrono::system_clock::time_point to_sys( time_point const& tp ) noexcept; +``` + +Returns: :: The `std::chrono::system_clock::time_point` corresponding to `tp`. + +Example: :: ++ +``` +#include +#include +#include +#include + +using namespace boost::uuids; + +int main() +{ + time_generator_v1 gen; + + uuid u = gen(); // generates a version 1 time-based UUID + + // note that stream output of std::chrono::system_clock::time_point requires C++20 + + std::cout << uuid_clock::to_sys( u.time_point_v1() ) << std::endl; +} +``` + +=== from_timestamp + +``` +static time_point from_timestamp( std::uint64_t timestamp ) noexcept; +``` + +Returns: :: The `uuid_clock::time_point` corresponding to `timestamp` 100 nanosecond intervals since the `uuid_clock` epoch. + +=== to_timestamp + +``` +static std::uint64_t to_timestamp( time_point const& tp ) noexcept; +``` + +Returns: :: The number of 100 nanosecond intervals since the `uuid_clock` epoch corresponding to `tp`. + +Example: :: ++ +``` +using namespace boost::uuids; + +uuid u = time_generator_v1()(); + +assert( u.timestamp_v1() == uuid_clock::to_timestamp( u.time_point_v1() ) ); +```