99#include < chrono>
1010#include < thread>
1111
12+ #include " ipaddress.h"
13+ #include " generate_hash.h"
14+
1215// ////////////////////////////////////////////////////////////////////////////////////////
1316
1417/* *
1518 * Total number of bits allocated to an ID
1619*/
17- int TOTAL_BITS = 64 ;
20+ constexpr int TOTAL_BITS = 64 ;
1821
1922/* *
2023 * Total number of bits allocated to an epoch timestamp
2124*/
22- int EPOCH_BITS = 42 ;
25+ constexpr int EPOCH_BITS = 42 ;
2326
2427/* *
2528 * Total number of bits allocated to an node/machine id
2629*/
27- int NODE_ID_BITS = 10 ;
30+ constexpr int NODE_ID_BITS = 12 ;
2831
2932/* *
3033 * Total number of bits allocated to an sequencing
3134*/
32- int SEQUENCE_BITS = 12 ;
35+ constexpr int SEQUENCE_BITS = 10 ;
3336
3437/* *
3538 * Max node that can be used
3639*/
37- uint64_t maxNodeId = std::pow( 2 , NODE_ID_BITS) - 1 ;
40+ constexpr uint64_t maxNodeId = ( 1 << NODE_ID_BITS) - 1 ;
3841
39- uint64_t maxSequence = std::pow( 2 , SEQUENCE_BITS) - 1 ;
42+ constexpr uint64_t maxSequence = ( 1 << SEQUENCE_BITS) - 1 ;
4043
4144// ////////////////////////////////////////////////////////////////////////////////////////
4245
@@ -46,9 +49,11 @@ uint64_t maxSequence = std::pow(2, SEQUENCE_BITS) - 1;
4649 * so that the hashed value is always smaller than maxNodeID
4750 * which is 10 bits in size
4851*/
49- int nodeID (std::string macID )
52+ int nodeID ()
5053{
51- return std::hash<std::string>()(macID) % maxNodeId;
54+ char ip[16 ];
55+ getIP (ip);
56+ return generate_hash (ip, 16 ) & maxNodeId;
5257}
5358
5459// ////////////////////////////////////////////////////////////////////////////////////////
@@ -69,19 +74,18 @@ class Snowflake : public Napi::ObjectWrap<Snowflake>
6974private:
7075 static Napi::FunctionReference constructor;
7176 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;
7680 Napi::Value getUniqueIDBigInt (const Napi::CallbackInfo &info);
77- Napi::Value getUniqueID (const Napi::CallbackInfo &info);
7881 Napi::Value getTimestampFromID (const Napi::CallbackInfo &info);
82+ Napi::Value getNodeIDFomID (const Napi::CallbackInfo &info);
7983};
8084
8185Napi::Object Snowflake::Init (Napi::Env env, Napi::Object exports)
8286{
8387 // 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 )});
8589
8690 // Create a peristent reference to the class constructor. This will allow
8791 // a function called on a class prototype and a function
@@ -97,14 +101,20 @@ Napi::Object Snowflake::Init(Napi::Env env, Napi::Object exports)
97101
98102Snowflake::Snowflake (const Napi::CallbackInfo &info) : Napi::ObjectWrap<Snowflake>(info)
99103{
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+ }
102115
103- this ->_macID = value.Utf8Value ();
104- this ->_nodeID = nodeID (this ->_macID );
105116 this ->_lastTimestamp = 0 ;
106117 this ->_sequence = 0 ;
107- this ->_CUSTOM_EPOCH = EPOCH.Int64Value ();
108118}
109119
110120Napi::FunctionReference Snowflake::constructor;
@@ -113,25 +123,25 @@ Napi::FunctionReference Snowflake::constructor;
113123 * Takes the current timestamp, last timestamp, sequence, and macID
114124 * and generates a 64 bit long integer by performing bitwise operations
115125 *
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 )
118128 * Next 12 bits are filled with sequence which ensures that even if timestamp didn't change the value will be generated
119129 *
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
121131*/
122132Napi::Value Snowflake::getUniqueIDBigInt (const Napi::CallbackInfo &info)
123133{
124- Napi::Env env = info.Env ();
134+ auto env = info.Env ();
125135
126136 uint64_t currentTimestamp = getCurrentTime () - this ->_CUSTOM_EPOCH ;
127137
128138 if (currentTimestamp == this ->_lastTimestamp )
129139 {
130140 this ->_sequence = (this ->_sequence + 1 ) & maxSequence;
131- if (this ->_sequence == 0 )
141+ if (! this ->_sequence )
132142 {
133143 std::this_thread::sleep_for (std::chrono::milliseconds (1 ));
134- currentTimestamp++ ;
144+ ++currentTimestamp ;
135145 }
136146 }
137147 else
@@ -141,58 +151,57 @@ Napi::Value Snowflake::getUniqueIDBigInt(const Napi::CallbackInfo &info)
141151
142152 this ->_lastTimestamp = currentTimestamp;
143153
144- uint64_t id = currentTimestamp << (TOTAL_BITS - EPOCH_BITS);
154+ uint64_t id{};
155+ id = currentTimestamp << (TOTAL_BITS - EPOCH_BITS);
145156 id |= (this ->_nodeID << (TOTAL_BITS - EPOCH_BITS - NODE_ID_BITS));
146157 id |= this ->_sequence ;
147158
148159 return Napi::BigInt::New (env, id);
149160}
150161
151162/* *
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
153166*/
154- Napi::Value Snowflake::getUniqueID (const Napi::CallbackInfo &info)
167+ Napi::Value Snowflake::getTimestampFromID (const Napi::CallbackInfo &info)
155168{
156- Napi::Env env = info.Env ();
169+ auto env = info.Env ();
170+ uint64_t uniqueID{};
157171
158- uint64_t currentTimestamp = getCurrentTime () - this ->_CUSTOM_EPOCH ;
172+ if (info[0 ].IsString ())
173+ {
174+ auto first = info[0 ].As <Napi::String>();
159175
160- if (currentTimestamp == this ->_lastTimestamp )
176+ uniqueID = std::stoull (first.Utf8Value ());
177+ }
178+ else if (info[0 ].IsNumber ())
161179 {
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 ();
168181 }
169182 else
170183 {
171- this -> _sequence = 0 ;
184+ Napi::TypeError::New (env, " Number or string expected " ). ThrowAsJavaScriptException () ;
172185 }
173186
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);
179188
180- return Napi::String ::New (env, std::to_string (id) );
189+ return Napi::Number ::New (env, timestamp + _CUSTOM_EPOCH );
181190}
182191
183192/* *
184193 * Returns timestamp at which the id was generated by retreiving
185194 * the first 42 bits of the id, which are filled with current timestamp
186195 * bits
187196*/
188- Napi::Value Snowflake::getTimestampFromID (const Napi::CallbackInfo &info)
197+ Napi::Value Snowflake::getNodeIDFomID (const Napi::CallbackInfo &info)
189198{
190- Napi::Env env = info.Env ();
199+ auto env = info.Env ();
191200 uint64_t uniqueID = 0 ;
192201
193202 if (info[0 ].IsString ())
194203 {
195- Napi::String first = info[0 ].As <Napi::String>();
204+ auto first = info[0 ].As <Napi::String>();
196205
197206 uniqueID = std::stoull (first.Utf8Value ());
198207 }
@@ -205,9 +214,10 @@ Napi::Value Snowflake::getTimestampFromID(const Napi::CallbackInfo &info)
205214 Napi::TypeError::New (env, " Number or string expected" ).ThrowAsJavaScriptException ();
206215 }
207216
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);
209219
210- return Napi::Number::New (env, timestamp );
220+ return Napi::Number::New (env, machineID );
211221}
212222
213223// ////////////////////////////////////////////////////////////////////////////////////////
0 commit comments