-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsnowflake.hpp
123 lines (104 loc) · 3.99 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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#ifndef ATOM_ALGORITHM_SNOWFLAKE_HPP
#define ATOM_ALGORITHM_SNOWFLAKE_HPP
#include <atomic>
#include <chrono>
#include <cstdint>
#include <mutex>
#include <random>
#include <stdexcept>
namespace atom::algorithm {
class SnowflakeNonLock {
public:
void lock() {}
void unlock() {}
};
template <uint64_t Twepoch, typename Lock = SnowflakeNonLock>
class Snowflake {
using lock_type = Lock;
static constexpr uint64_t TWEPOCH = Twepoch;
static constexpr uint64_t WORKER_ID_BITS = 5;
static constexpr uint64_t DATACENTER_ID_BITS = 5;
static constexpr uint64_t MAX_WORKER_ID = (1ULL << WORKER_ID_BITS) - 1;
static constexpr uint64_t MAX_DATACENTER_ID =
(1ULL << DATACENTER_ID_BITS) - 1;
static constexpr uint64_t SEQUENCE_BITS = 12;
static constexpr uint64_t WORKER_ID_SHIFT = SEQUENCE_BITS;
static constexpr uint64_t DATACENTER_ID_SHIFT =
SEQUENCE_BITS + WORKER_ID_BITS;
static constexpr uint64_t TIMESTAMP_LEFT_SHIFT =
SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
static constexpr uint64_t SEQUENCE_MASK = (1ULL << SEQUENCE_BITS) - 1;
using time_point = std::chrono::time_point<std::chrono::steady_clock>;
time_point start_time_point_ = std::chrono::steady_clock::now();
uint64_t start_millisecond_ =
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
std::atomic<uint64_t> last_timestamp_{0};
uint64_t workerid_ = 0;
uint64_t datacenterid_ = 0;
uint64_t sequence_ = 0;
lock_type lock_;
uint64_t secret_key_;
public:
Snowflake() {
std::random_device rd;
std::mt19937_64 eng(rd());
std::uniform_int_distribution<uint64_t> distr;
secret_key_ = distr(eng);
}
Snowflake(const Snowflake &) = delete;
auto operator=(const Snowflake &) -> Snowflake & = delete;
void init(uint64_t worker_id, uint64_t datacenter_id) {
if (worker_id > MAX_WORKER_ID) {
throw std::runtime_error("worker Id can't be greater than 31");
}
if (datacenter_id > MAX_DATACENTER_ID) {
throw std::runtime_error("datacenter Id can't be greater than 31");
}
workerid_ = worker_id;
datacenterid_ = datacenter_id;
}
[[nodiscard]] auto nextid() -> uint64_t {
std::lock_guard<lock_type> lock(lock_);
auto timestamp = millisecond();
if (last_timestamp_.load() == timestamp) {
sequence_ = (sequence_ + 1) & SEQUENCE_MASK;
if (sequence_ == 0) {
timestamp = waitNextMillis(last_timestamp_.load());
}
} else {
sequence_ = 0;
}
last_timestamp_.store(timestamp);
uint64_t id = ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) |
(datacenterid_ << DATACENTER_ID_SHIFT) |
(workerid_ << WORKER_ID_SHIFT) | sequence_;
return id ^ secret_key_;
}
void parseId(uint64_t encrypted_id, uint64_t ×tamp,
uint64_t &datacenter_id, uint64_t &worker_id,
uint64_t &sequence) const {
uint64_t id = encrypted_id ^ secret_key_;
timestamp = (id >> TIMESTAMP_LEFT_SHIFT) + TWEPOCH;
datacenter_id = (id >> DATACENTER_ID_SHIFT) & MAX_DATACENTER_ID;
worker_id = (id >> WORKER_ID_SHIFT) & MAX_WORKER_ID;
sequence = id & SEQUENCE_MASK;
}
private:
[[nodiscard]] auto millisecond() const noexcept -> uint64_t {
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start_time_point_);
return start_millisecond_ + diff.count();
}
[[nodiscard]] auto waitNextMillis(uint64_t last) const noexcept
-> uint64_t {
auto timestamp = millisecond();
while (timestamp <= last) {
timestamp = millisecond();
}
return timestamp;
}
};
} // namespace atom::algorithm
#endif // ATOM_ALGORITHM_SNOWFLAKE_HPP