time_generator_v7.hpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. #ifndef BOOST_UUID_TIME_GENERATOR_V7_HPP_INCLUDED
  2. #define BOOST_UUID_TIME_GENERATOR_V7_HPP_INCLUDED
  3. // Copyright 2024 Peter Dimov
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // https://www.boost.org/LICENSE_1_0.txt
  6. #include <boost/uuid/uuid.hpp>
  7. #include <boost/uuid/detail/chacha20.hpp>
  8. #include <boost/uuid/detail/random_provider.hpp>
  9. #include <boost/uuid/detail/endian.hpp>
  10. #include <random>
  11. #include <cstdint>
  12. #include <cstring>
  13. namespace boost {
  14. namespace uuids {
  15. // time_generator_v7
  16. class time_generator_v7
  17. {
  18. private:
  19. // Bit layout from high to low:
  20. // 48 bits: millisecond part of Unix epoch timestamp
  21. // 10 bits: microsecond part of Unix epoch timestamp
  22. // 6 bits: conflict resolution counter
  23. using state_type = std::uint64_t;
  24. state_type state_ = {};
  25. detail::chacha20_12 rng_;
  26. public:
  27. using result_type = uuid;
  28. time_generator_v7();
  29. time_generator_v7( time_generator_v7 const& rhs );
  30. time_generator_v7( time_generator_v7&& rhs ) noexcept;
  31. time_generator_v7& operator=( time_generator_v7 const& rhs ) noexcept;
  32. time_generator_v7& operator=( time_generator_v7&& rhs ) noexcept;
  33. result_type operator()() noexcept;
  34. private:
  35. static state_type get_new_state( state_type const& oldst ) noexcept;
  36. };
  37. // constructors
  38. inline time_generator_v7::time_generator_v7()
  39. {
  40. detail::random_provider seeder;
  41. rng_.seed( seeder );
  42. }
  43. inline time_generator_v7::time_generator_v7( time_generator_v7 const& rhs ): state_( rhs.state_ )
  44. {
  45. detail::random_provider seeder;
  46. rng_.seed( seeder );
  47. }
  48. inline time_generator_v7::time_generator_v7( time_generator_v7&& rhs ) noexcept: state_( std::move( rhs.state_ ) ), rng_( std::move( rhs.rng_ ) )
  49. {
  50. rhs.rng_.perturb();
  51. }
  52. // assignment
  53. inline time_generator_v7& time_generator_v7::operator=( time_generator_v7 const& rhs ) noexcept
  54. {
  55. state_ = rhs.state_;
  56. return *this;
  57. }
  58. inline time_generator_v7& time_generator_v7::operator=( time_generator_v7&& rhs ) noexcept
  59. {
  60. state_ = std::move( rhs.state_ );
  61. rng_ = std::move( rhs.rng_ );
  62. rhs.rng_.perturb();
  63. return *this;
  64. }
  65. // get_new_state
  66. inline time_generator_v7::state_type time_generator_v7::get_new_state( state_type const& oldst ) noexcept
  67. {
  68. // `now()` in microseconds
  69. std::uint64_t now_in_us = std::chrono::time_point_cast< std::chrono::microseconds >( std::chrono::system_clock::now() ).time_since_epoch().count();
  70. std::uint64_t time_ms = now_in_us / 1000; // timestamp, ms part
  71. std::uint64_t time_us = now_in_us % 1000; // timestamp, us part
  72. std::uint64_t newst = ( time_ms << 16 ) | ( time_us << 6 );
  73. // if the time has advanced, reset counter to zero
  74. if( newst > oldst )
  75. {
  76. return newst;
  77. }
  78. // if time_in_ms has gone backwards, we can't be monotonic
  79. if( time_ms < ( oldst >> 16 ) )
  80. {
  81. return newst;
  82. }
  83. // otherwise, use the old value and increment the counter
  84. return oldst + 1;
  85. }
  86. // operator()
  87. inline time_generator_v7::result_type time_generator_v7::operator()() noexcept
  88. {
  89. uuid result;
  90. // set lower 64 bits to random values
  91. std::uniform_int_distribution<std::uint32_t> dist;
  92. detail::store_native_u32( result.data + 8, dist( rng_ ) );
  93. detail::store_native_u32( result.data + 12, dist( rng_ ) );
  94. // get new timestamp
  95. state_ = get_new_state( state_ );
  96. std::uint64_t time_ms = state_ >> 16; // timestamp, ms part
  97. std::uint64_t time_us = ( state_ & 0xFFFF ) >> 6; // timestamp, us part
  98. std::uint64_t timestamp = ( time_ms << 16 ) | 0x7000 | time_us;
  99. detail::store_big_u64( result.data + 0, timestamp );
  100. // set variant and counter
  101. result.data[ 8 ] = static_cast< std::uint8_t >( 0x80 | ( state_ & 0x3F ) );
  102. return result;
  103. }
  104. }} // namespace boost::uuids
  105. #endif // BOOST_UUID_TIME_GENERATOR_V7_HPP_INCLUDED