-
Notifications
You must be signed in to change notification settings - Fork 24
/
snowflake.hpp
106 lines (91 loc) · 3.13 KB
/
snowflake.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#pragma once
#include <cstdint>
#include <chrono>
#include <stdexcept>
#include <mutex>
class snowflake_nonlock
{
public:
void lock()
{
}
void unlock()
{
}
};
template<int64_t Twepoch, typename Lock = snowflake_nonlock>
class snowflake
{
using lock_type = Lock;
static constexpr int64_t TWEPOCH = Twepoch;
static constexpr int64_t WORKER_ID_BITS = 5L;
static constexpr int64_t DATACENTER_ID_BITS = 5L;
static constexpr int64_t MAX_WORKER_ID = (1 << WORKER_ID_BITS) - 1;
static constexpr int64_t MAX_DATACENTER_ID = (1 << DATACENTER_ID_BITS) - 1;
static constexpr int64_t SEQUENCE_BITS = 12L;
static constexpr int64_t WORKER_ID_SHIFT = SEQUENCE_BITS;
static constexpr int64_t DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
static constexpr int64_t TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
static constexpr int64_t SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1;
using time_point = std::chrono::time_point<std::chrono::steady_clock>;
time_point start_time_point_ = std::chrono::steady_clock::now();
int64_t start_millsecond_ = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
int64_t last_timestamp_ = -1;
int64_t workerid_ = 0;
int64_t datacenterid_ = 0;
int64_t sequence_ = 0;
lock_type lock_;
public:
snowflake() = default;
snowflake(const snowflake&) = delete;
snowflake& operator=(const snowflake&) = delete;
void init(int64_t workerid, int64_t datacenterid)
{
if (workerid > MAX_WORKER_ID || workerid < 0) {
throw std::runtime_error("worker Id can't be greater than 31 or less than 0");
}
if (datacenterid > MAX_DATACENTER_ID || datacenterid < 0) {
throw std::runtime_error("datacenter Id can't be greater than 31 or less than 0");
}
workerid_ = workerid;
datacenterid_ = datacenterid;
}
int64_t nextid()
{
std::lock_guard<lock_type> lock(lock_);
//std::chrono::steady_clock cannot decrease as physical time moves forward
auto timestamp = millsecond();
if (last_timestamp_ == timestamp)
{
sequence_ = (sequence_ + 1)&SEQUENCE_MASK;
if (sequence_ == 0)
{
timestamp = wait_next_millis(last_timestamp_);
}
}
else
{
sequence_ = 0;
}
last_timestamp_ = timestamp;
return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)
| (datacenterid_ << DATACENTER_ID_SHIFT)
| (workerid_ << WORKER_ID_SHIFT)
| sequence_;
}
private:
int64_t millsecond() const noexcept
{
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time_point_);
return start_millsecond_ + diff.count();
}
int64_t wait_next_millis(int64_t last) const noexcept
{
auto timestamp = millsecond();
while (timestamp <= last)
{
timestamp = millsecond();
}
return timestamp;
}
};