9
9
#include < chrono>
10
10
#include < thread>
11
11
12
+ #include " ipaddress.h"
13
+ #include " generate_hash.h"
14
+
12
15
// ////////////////////////////////////////////////////////////////////////////////////////
13
16
14
17
/* *
15
18
* Total number of bits allocated to an ID
16
19
*/
17
- int TOTAL_BITS = 64 ;
20
+ constexpr int TOTAL_BITS = 64 ;
18
21
19
22
/* *
20
23
* Total number of bits allocated to an epoch timestamp
21
24
*/
22
- int EPOCH_BITS = 42 ;
25
+ constexpr int EPOCH_BITS = 42 ;
23
26
24
27
/* *
25
28
* Total number of bits allocated to an node/machine id
26
29
*/
27
- int NODE_ID_BITS = 10 ;
30
+ constexpr int NODE_ID_BITS = 12 ;
28
31
29
32
/* *
30
33
* Total number of bits allocated to an sequencing
31
34
*/
32
- int SEQUENCE_BITS = 12 ;
35
+ constexpr int SEQUENCE_BITS = 10 ;
33
36
34
37
/* *
35
38
* Max node that can be used
36
39
*/
37
- uint64_t maxNodeId = std::pow( 2 , NODE_ID_BITS) - 1 ;
40
+ constexpr uint64_t maxNodeId = ( 1 << NODE_ID_BITS) - 1 ;
38
41
39
- uint64_t maxSequence = std::pow( 2 , SEQUENCE_BITS) - 1 ;
42
+ constexpr uint64_t maxSequence = ( 1 << SEQUENCE_BITS) - 1 ;
40
43
41
44
// ////////////////////////////////////////////////////////////////////////////////////////
42
45
@@ -46,9 +49,11 @@ uint64_t maxSequence = std::pow(2, SEQUENCE_BITS) - 1;
46
49
* so that the hashed value is always smaller than maxNodeID
47
50
* which is 10 bits in size
48
51
*/
49
- int nodeID (std::string macID )
52
+ int nodeID ()
50
53
{
51
- return std::hash<std::string>()(macID) % maxNodeId;
54
+ char ip[16 ];
55
+ getIP (ip);
56
+ return generate_hash (ip, 16 ) & maxNodeId;
52
57
}
53
58
54
59
// ////////////////////////////////////////////////////////////////////////////////////////
@@ -69,19 +74,18 @@ class Snowflake : public Napi::ObjectWrap<Snowflake>
69
74
private:
70
75
static Napi::FunctionReference constructor;
71
76
uint64_t _lastTimestamp;
72
- uint64_t _CUSTOM_EPOCH;
73
- int _sequence;
74
- std::string _macID;
75
- int _nodeID;
77
+ uint64_t _CUSTOM_EPOCH = 1546300800000 ;
78
+ uint16_t _sequence;
79
+ uint16_t _nodeID;
76
80
Napi::Value getUniqueIDBigInt (const Napi::CallbackInfo &info);
77
- Napi::Value getUniqueID (const Napi::CallbackInfo &info);
78
81
Napi::Value getTimestampFromID (const Napi::CallbackInfo &info);
82
+ Napi::Value getNodeIDFomID (const Napi::CallbackInfo &info);
79
83
};
80
84
81
85
Napi::Object Snowflake::Init (Napi::Env env, Napi::Object exports)
82
86
{
83
87
// This method is used to hook the accessor and method callbacks
84
- Napi::Function func = DefineClass (env, " Snowflake" , {InstanceMethod (" getUniqueIDBigInt " , &Snowflake::getUniqueIDBigInt), InstanceMethod (" getUniqueID " , &Snowflake::getUniqueID ), InstanceMethod (" getTimestampFromID " , &Snowflake::getTimestampFromID )});
88
+ auto func = DefineClass (env, " Snowflake" , {InstanceMethod (" getUniqueID " , &Snowflake::getUniqueIDBigInt), InstanceMethod (" getTimestampFromID " , &Snowflake::getTimestampFromID ), InstanceMethod (" getNodeIDFromID " , &Snowflake::getNodeIDFomID )});
85
89
86
90
// Create a peristent reference to the class constructor. This will allow
87
91
// a function called on a class prototype and a function
@@ -97,14 +101,20 @@ Napi::Object Snowflake::Init(Napi::Env env, Napi::Object exports)
97
101
98
102
Snowflake::Snowflake (const Napi::CallbackInfo &info) : Napi::ObjectWrap<Snowflake>(info)
99
103
{
100
- Napi::String value = info[0 ].As <Napi::String>();
101
- Napi::Number EPOCH = info[1 ].As <Napi::Number>();
104
+ auto argLen = info.Length ();
105
+ this ->_CUSTOM_EPOCH = info[0 ].As <Napi::Number>().Int64Value ();
106
+ switch (argLen)
107
+ {
108
+ case 2 :
109
+ this ->_nodeID = info[1 ].As <Napi::Number>().Int32Value () & maxNodeId;
110
+ break ;
111
+ default :
112
+ this ->_nodeID = nodeID ();
113
+ break ;
114
+ }
102
115
103
- this ->_macID = value.Utf8Value ();
104
- this ->_nodeID = nodeID (this ->_macID );
105
116
this ->_lastTimestamp = 0 ;
106
117
this ->_sequence = 0 ;
107
- this ->_CUSTOM_EPOCH = EPOCH.Int64Value ();
108
118
}
109
119
110
120
Napi::FunctionReference Snowflake::constructor;
@@ -113,25 +123,25 @@ Napi::FunctionReference Snowflake::constructor;
113
123
* Takes the current timestamp, last timestamp, sequence, and macID
114
124
* and generates a 64 bit long integer by performing bitwise operations
115
125
*
116
- * First 42 bits are filled with current timestamp
117
- * Next 10 bits are filled with the node/machine id (max size can be 1024 )
126
+ * First 41 bits are filled with current timestamp
127
+ * Next 10 bits are filled with the node/machine id (max size can be 4096 )
118
128
* Next 12 bits are filled with sequence which ensures that even if timestamp didn't change the value will be generated
119
129
*
120
- * Function can theorotically generate 4096 unique values within a millisecond without repeating values
130
+ * Function can theorotically generate 1024 unique values within a millisecond without repeating values
121
131
*/
122
132
Napi::Value Snowflake::getUniqueIDBigInt (const Napi::CallbackInfo &info)
123
133
{
124
- Napi::Env env = info.Env ();
134
+ auto env = info.Env ();
125
135
126
136
uint64_t currentTimestamp = getCurrentTime () - this ->_CUSTOM_EPOCH ;
127
137
128
138
if (currentTimestamp == this ->_lastTimestamp )
129
139
{
130
140
this ->_sequence = (this ->_sequence + 1 ) & maxSequence;
131
- if (this ->_sequence == 0 )
141
+ if (! this ->_sequence )
132
142
{
133
143
std::this_thread::sleep_for (std::chrono::milliseconds (1 ));
134
- currentTimestamp++ ;
144
+ ++currentTimestamp ;
135
145
}
136
146
}
137
147
else
@@ -141,58 +151,57 @@ Napi::Value Snowflake::getUniqueIDBigInt(const Napi::CallbackInfo &info)
141
151
142
152
this ->_lastTimestamp = currentTimestamp;
143
153
144
- uint64_t id = currentTimestamp << (TOTAL_BITS - EPOCH_BITS);
154
+ uint64_t id{};
155
+ id = currentTimestamp << (TOTAL_BITS - EPOCH_BITS);
145
156
id |= (this ->_nodeID << (TOTAL_BITS - EPOCH_BITS - NODE_ID_BITS));
146
157
id |= this ->_sequence ;
147
158
148
159
return Napi::BigInt::New (env, id);
149
160
}
150
161
151
162
/* *
152
- * Convert generated number 64 bit integer to a string
163
+ * Returns timestamp at which the id was generated by retreiving
164
+ * the first 41 bits of the id, which are filled with current timestamp
165
+ * bits
153
166
*/
154
- Napi::Value Snowflake::getUniqueID (const Napi::CallbackInfo &info)
167
+ Napi::Value Snowflake::getTimestampFromID (const Napi::CallbackInfo &info)
155
168
{
156
- Napi::Env env = info.Env ();
169
+ auto env = info.Env ();
170
+ uint64_t uniqueID{};
157
171
158
- uint64_t currentTimestamp = getCurrentTime () - this ->_CUSTOM_EPOCH ;
172
+ if (info[0 ].IsString ())
173
+ {
174
+ auto first = info[0 ].As <Napi::String>();
159
175
160
- if (currentTimestamp == this ->_lastTimestamp )
176
+ uniqueID = std::stoull (first.Utf8Value ());
177
+ }
178
+ else if (info[0 ].IsNumber ())
161
179
{
162
- this ->_sequence = (this ->_sequence + 1 ) & maxSequence;
163
- if (this ->_sequence == 0 )
164
- {
165
- std::this_thread::sleep_for (std::chrono::milliseconds (1 ));
166
- currentTimestamp++;
167
- }
180
+ uniqueID = info[0 ].As <Napi::Number>().Int64Value ();
168
181
}
169
182
else
170
183
{
171
- this -> _sequence = 0 ;
184
+ Napi::TypeError::New (env, " Number or string expected " ). ThrowAsJavaScriptException () ;
172
185
}
173
186
174
- this ->_lastTimestamp = currentTimestamp;
175
-
176
- uint64_t id = currentTimestamp << (TOTAL_BITS - EPOCH_BITS);
177
- id |= (this ->_nodeID << (TOTAL_BITS - EPOCH_BITS - NODE_ID_BITS));
178
- id |= this ->_sequence ;
187
+ uint64_t timestamp = uniqueID >> (TOTAL_BITS - EPOCH_BITS);
179
188
180
- return Napi::String ::New (env, std::to_string (id) );
189
+ return Napi::Number ::New (env, timestamp + _CUSTOM_EPOCH );
181
190
}
182
191
183
192
/* *
184
193
* Returns timestamp at which the id was generated by retreiving
185
194
* the first 42 bits of the id, which are filled with current timestamp
186
195
* bits
187
196
*/
188
- Napi::Value Snowflake::getTimestampFromID (const Napi::CallbackInfo &info)
197
+ Napi::Value Snowflake::getNodeIDFomID (const Napi::CallbackInfo &info)
189
198
{
190
- Napi::Env env = info.Env ();
199
+ auto env = info.Env ();
191
200
uint64_t uniqueID = 0 ;
192
201
193
202
if (info[0 ].IsString ())
194
203
{
195
- Napi::String first = info[0 ].As <Napi::String>();
204
+ auto first = info[0 ].As <Napi::String>();
196
205
197
206
uniqueID = std::stoull (first.Utf8Value ());
198
207
}
@@ -205,9 +214,10 @@ Napi::Value Snowflake::getTimestampFromID(const Napi::CallbackInfo &info)
205
214
Napi::TypeError::New (env, " Number or string expected" ).ThrowAsJavaScriptException ();
206
215
}
207
216
208
- uint64_t timestamp = uniqueID >> (TOTAL_BITS - EPOCH_BITS);
217
+ int BITS = TOTAL_BITS - NODE_ID_BITS - SEQUENCE_BITS;
218
+ uint16_t machineID = (uniqueID << BITS) >> (BITS + SEQUENCE_BITS);
209
219
210
- return Napi::Number::New (env, timestamp );
220
+ return Napi::Number::New (env, machineID );
211
221
}
212
222
213
223
// ////////////////////////////////////////////////////////////////////////////////////////
0 commit comments