2
0
mirror of https://github.com/boostorg/uuid.git synced 2026-01-19 04:42:16 +00:00

Add time_generator_v1

This commit is contained in:
Peter Dimov
2024-04-24 01:52:59 +03:00
parent d7aca60d72
commit abfba7f6a4
5 changed files with 236 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
#ifndef BOOST_UUID_TIME_GENERATOR_V1_HPP_INCLUDED
#define BOOST_UUID_TIME_GENERATOR_V1_HPP_INCLUDED
// Copyright 2024 Peter Dimov
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/uuid/detail/uuid_clock.hpp>
#include <boost/uuid/detail/random_provider.hpp>
#include <boost/uuid/detail/endian.hpp>
#include <boost/uuid/uuid.hpp>
#include <atomic>
#include <array>
#include <cstdint>
#include <cstring>
namespace boost {
namespace uuids {
// time_generator_v1
class time_generator_v1
{
public:
using node_type = std::array<std::uint8_t, 6>;
struct state_type
{
std::uint64_t timestamp;
std::uint16_t clock_seq;
};
private:
node_type node_ = {};
std::atomic<state_type>* ps_ = nullptr;
state_type state_ = {};
public:
using result_type = uuid;
time_generator_v1();
time_generator_v1( node_type const& node, std::atomic<state_type>& state ) noexcept;
result_type operator()() noexcept;
private:
static state_type get_new_state( state_type const& oldst ) noexcept;
};
// constructors
inline time_generator_v1::time_generator_v1()
{
detail::random_provider prov;
// generate a pseudorandom node identifier
std::uint32_t tmp[ 3 ];
prov.generate( tmp, tmp + 3 );
std::memcpy( node_.data(), tmp, node_.size() );
node_[ 0 ] |= 0x01; // mark as multicast
// generate a pseudorandom 14 bit clock sequence
state_.clock_seq = static_cast<std::uint16_t>( tmp[ 2 ] & 0x3FFF );
}
inline time_generator_v1::time_generator_v1( node_type const& node, std::atomic<state_type>& state ) noexcept: node_( node ), ps_( &state )
{
}
// get_new_state
inline time_generator_v1::state_type time_generator_v1::get_new_state( state_type const& oldst ) noexcept
{
state_type newst( oldst );
std::uint64_t timestamp = detail::uuid_clock::now().time_since_epoch().count();
if( timestamp <= newst.timestamp )
{
newst.clock_seq = ( newst.clock_seq + 1 ) & 0x3FFF;
}
newst.timestamp = timestamp;
return newst;
}
// operator()
inline time_generator_v1::result_type time_generator_v1::operator()() noexcept
{
if( ps_ )
{
auto oldst = ps_->load( std::memory_order_relaxed );
for( ;; )
{
auto newst = get_new_state( oldst );
if( ps_->compare_exchange_strong( oldst, newst, std::memory_order_relaxed, std::memory_order_relaxed ) )
{
state_ = newst;
break;
}
}
}
else
{
state_ = get_new_state( state_ );
}
uuid result;
std::uint32_t time_low = static_cast< std::uint32_t >( state_.timestamp );
detail::store_big_u32( result.data + 0, time_low );
std::uint16_t time_mid = static_cast< std::uint16_t >( state_.timestamp >> 32 );
detail::store_big_u16( result.data + 4, time_mid );
std::uint16_t time_hi_and_version = static_cast< std::uint16_t >( state_.timestamp >> 48 ) | 0x1000;
detail::store_big_u16( result.data + 6, time_hi_and_version );
detail::store_big_u16( result.data + 8, state_.clock_seq | 0x8000 );
std::memcpy( result.data + 10, node_.data(), 6 );
return result;
}
}} // namespace boost::uuids
#endif // BOOST_UUID_TIME_GENERATOR_V1_HPP_INCLUDED

View File

@@ -17,5 +17,6 @@
#include <boost/uuid/name_generator_md5.hpp>
#include <boost/uuid/name_generator.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/time_generator_v1.hpp>
#endif //BOOST_UUID_UUID_GENERATORS_HPP_INCLUDED

View File

@@ -25,6 +25,7 @@ boost_test(TYPE run SOURCES test_nil_generator.cpp)
boost_test(TYPE run SOURCES test_name_generator.cpp)
boost_test(TYPE run SOURCES test_string_generator.cpp)
boost_test(TYPE run SOURCES test_random_generator.cpp LINK_LIBRARIES Boost::random)
boost_test(TYPE run SOURCES test_time_generator_v1.cpp)
boost_test(TYPE run SOURCES test_tagging.cpp)

View File

@@ -83,6 +83,7 @@ run test_nil_generator.cpp ;
run test_name_generator.cpp ;
run test_string_generator.cpp ;
run test_random_generator.cpp ;
run test_time_generator_v1.cpp ;
# test serializing uuids

View File

@@ -0,0 +1,90 @@
// Copyright 2024 Peter Dimov
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/uuid/time_generator_v1.hpp>
#include <boost/uuid/time_generator_v1.hpp>
#include <boost/core/lightweight_test.hpp>
#include <atomic>
#include <set>
#include <cstdint>
#include <cstring>
using namespace boost::uuids;
time_generator_v1::node_type get_node( uuid const& u )
{
time_generator_v1::node_type node = {};
std::memcpy( node.data(), u.data + 10, 6 );
return node;
}
std::uint16_t get_clock_seq( uuid const& u )
{
return detail::load_big_u16( u.data + 8 ) & 0x3FFF;
}
int main()
{
int const N = 1024;
{
std::set<uuid> set;
time_generator_v1 gen;
uuid u1 = gen();
BOOST_TEST_EQ( u1.variant(), uuid::variant_rfc_4122 );
BOOST_TEST_EQ( u1.version(), uuid::version_time_based );
set.insert( u1 );
for( int i = 0; i < N; ++i )
{
uuid u2 = gen();
BOOST_TEST_EQ( u2.variant(), uuid::variant_rfc_4122 );
BOOST_TEST_EQ( u2.version(), uuid::version_time_based );
BOOST_TEST( get_node( u1 ) == get_node( u2 ) );
set.insert( u2 );
}
BOOST_TEST_EQ( set.size(), N + 1 );
}
{
std::set<uuid> set;
time_generator_v1::node_type node{{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }};
std::atomic< time_generator_v1::state_type > state{{ 0, 0x2222 }};
time_generator_v1 gen( node, state );
uuid u1 = gen();
BOOST_TEST_EQ( u1.variant(), uuid::variant_rfc_4122 );
BOOST_TEST_EQ( u1.version(), uuid::version_time_based );
BOOST_TEST( get_node( u1 ) == node );
BOOST_TEST_EQ( get_clock_seq( u1 ), 0x2222 );
set.insert( u1 );
for( int i = 0; i < N; ++i )
{
uuid u2 = gen();
BOOST_TEST_EQ( u2.variant(), uuid::variant_rfc_4122 );
BOOST_TEST_EQ( u2.version(), uuid::version_time_based );
BOOST_TEST( get_node( u1 ) == get_node( u2 ) );
set.insert( u2 );
}
BOOST_TEST_EQ( set.size(), N + 1 );
}
return boost::report_errors();
}