time_generator_v1.hpp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. #ifndef BOOST_UUID_TIME_GENERATOR_V1_HPP_INCLUDED
  2. #define BOOST_UUID_TIME_GENERATOR_V1_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/uuid_clock.hpp>
  8. #include <boost/uuid/detail/random_provider.hpp>
  9. #include <boost/uuid/detail/endian.hpp>
  10. #include <boost/config.hpp>
  11. #include <boost/config/workaround.hpp>
  12. #include <atomic>
  13. #include <cstdint>
  14. #include <cstring>
  15. namespace boost {
  16. namespace uuids {
  17. // time_generator_v1
  18. class time_generator_v1
  19. {
  20. public:
  21. struct state_type
  22. {
  23. std::uint64_t timestamp;
  24. std::uint16_t clock_seq;
  25. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114865
  26. #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION, >= 130000)
  27. # if BOOST_CXX_VERSION >= 201402L
  28. std::uint16_t padding[ 3 ] = {};
  29. # else
  30. std::uint16_t padding[ 3 ];
  31. # endif
  32. #endif
  33. };
  34. private:
  35. uuid::node_type node_ = {{}};
  36. std::atomic<state_type>* ps_ = nullptr;
  37. #if BOOST_WORKAROUND(BOOST_GCC, < 50000)
  38. // Avoid -Wmissing-field-initializers under GCC 4.x
  39. state_type state_ = { 0, 0 };
  40. #else
  41. state_type state_ = {};
  42. #endif
  43. public:
  44. using result_type = uuid;
  45. time_generator_v1();
  46. time_generator_v1( uuid::node_type const& node, state_type const& state ) noexcept;
  47. time_generator_v1( uuid::node_type const& node, std::atomic<state_type>& state ) noexcept;
  48. result_type operator()() noexcept;
  49. private:
  50. static state_type get_new_state( state_type const& oldst ) noexcept;
  51. };
  52. // constructors
  53. inline time_generator_v1::time_generator_v1()
  54. {
  55. detail::random_provider prov;
  56. // generate a pseudorandom node identifier
  57. std::uint32_t tmp[ 3 ];
  58. prov.generate( tmp, tmp + 3 );
  59. std::memcpy( node_.data(), tmp, node_.size() );
  60. node_[ 0 ] |= 0x01; // mark as multicast
  61. // generate a pseudorandom 14 bit clock sequence
  62. state_.clock_seq = static_cast<std::uint16_t>( tmp[ 2 ] & 0x3FFF );
  63. }
  64. inline time_generator_v1::time_generator_v1( uuid::node_type const& node, state_type const& state ) noexcept: node_( node ), state_( state )
  65. {
  66. }
  67. inline time_generator_v1::time_generator_v1( uuid::node_type const& node, std::atomic<state_type>& state ) noexcept: node_( node ), ps_( &state )
  68. {
  69. }
  70. // get_new_state
  71. inline time_generator_v1::state_type time_generator_v1::get_new_state( state_type const& oldst ) noexcept
  72. {
  73. state_type newst( oldst );
  74. std::uint64_t timestamp = uuid_clock::now().time_since_epoch().count();
  75. if( timestamp <= newst.timestamp )
  76. {
  77. newst.clock_seq = ( newst.clock_seq + 1 ) & 0x3FFF;
  78. }
  79. newst.timestamp = timestamp;
  80. return newst;
  81. }
  82. // operator()
  83. inline time_generator_v1::result_type time_generator_v1::operator()() noexcept
  84. {
  85. if( ps_ )
  86. {
  87. auto oldst = ps_->load( std::memory_order_relaxed );
  88. for( ;; )
  89. {
  90. auto newst = get_new_state( oldst );
  91. if( ps_->compare_exchange_strong( oldst, newst, std::memory_order_relaxed, std::memory_order_relaxed ) )
  92. {
  93. state_ = newst;
  94. break;
  95. }
  96. }
  97. }
  98. else
  99. {
  100. state_ = get_new_state( state_ );
  101. }
  102. uuid result;
  103. std::uint32_t time_low = static_cast< std::uint32_t >( state_.timestamp );
  104. detail::store_big_u32( result.data + 0, time_low );
  105. std::uint16_t time_mid = static_cast< std::uint16_t >( state_.timestamp >> 32 );
  106. detail::store_big_u16( result.data + 4, time_mid );
  107. std::uint16_t time_hi_and_version = static_cast< std::uint16_t >( state_.timestamp >> 48 ) | 0x1000;
  108. detail::store_big_u16( result.data + 6, time_hi_and_version );
  109. detail::store_big_u16( result.data + 8, state_.clock_seq | 0x8000 );
  110. std::memcpy( result.data + 10, node_.data(), 6 );
  111. return result;
  112. }
  113. }} // namespace boost::uuids
  114. #endif // BOOST_UUID_TIME_GENERATOR_V1_HPP_INCLUDED