UNPKG

4.65 kBJavaScriptView Raw
1// Unique ID creation requires a high quality random # generator. We feature
2// detect to determine the best RNG source, normalizing to a function that
3// returns 128-bits of randomness, since that's what's usually required
4var _rng = require('./lib/rng');
5
6// Maps for number <-> hex string conversion
7var _byteToHex = [];
8var _hexToByte = {};
9for (var i = 0; i < 256; ++i) {
10 _byteToHex[i] = (i + 0x100).toString(16).substr(1);
11 _hexToByte[_byteToHex[i]] = i;
12}
13
14function buff_to_string(buf, offset) {
15 var i = offset || 0;
16 var bth = _byteToHex;
17 return bth[buf[i++]] + bth[buf[i++]] +
18 bth[buf[i++]] + bth[buf[i++]] + '-' +
19 bth[buf[i++]] + bth[buf[i++]] + '-' +
20 bth[buf[i++]] + bth[buf[i++]] + '-' +
21 bth[buf[i++]] + bth[buf[i++]] + '-' +
22 bth[buf[i++]] + bth[buf[i++]] +
23 bth[buf[i++]] + bth[buf[i++]] +
24 bth[buf[i++]] + bth[buf[i++]];
25}
26
27// **`v1()` - Generate time-based UUID**
28//
29// Inspired by https://github.com/LiosK/UUID.js
30// and http://docs.python.org/library/uuid.html
31
32// random #'s we need to init node and clockseq
33var _seedBytes = _rng();
34
35// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
36var _nodeId = [
37 _seedBytes[0] | 0x01,
38 _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]
39];
40
41// Per 4.2.2, randomize (14 bit) clockseq
42var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;
43
44// Previous uuid creation time
45var _lastMSecs = 0, _lastNSecs = 0;
46
47// See https://github.com/broofa/node-uuid for API details
48function v1(options, buf, offset) {
49 var i = buf && offset || 0;
50 var b = buf || [];
51
52 options = options || {};
53
54 var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq;
55
56 // UUID timestamps are 100 nano-second units since the Gregorian epoch,
57 // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so
58 // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
59 // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
60 var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime();
61
62 // Per 4.2.1.2, use count of uuid's generated during the current clock
63 // cycle to simulate higher resolution clock
64 var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1;
65
66 // Time since last uuid creation (in msecs)
67 var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;
68
69 // Per 4.2.1.2, Bump clockseq on clock regression
70 if (dt < 0 && options.clockseq === undefined) {
71 clockseq = clockseq + 1 & 0x3fff;
72 }
73
74 // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
75 // time interval
76 if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {
77 nsecs = 0;
78 }
79
80 // Per 4.2.1.2 Throw error if too many uuids are requested
81 if (nsecs >= 10000) {
82 throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
83 }
84
85 _lastMSecs = msecs;
86 _lastNSecs = nsecs;
87 _clockseq = clockseq;
88
89 // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
90 msecs += 12219292800000;
91
92 // `time_low`
93 var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
94 b[i++] = tl >>> 24 & 0xff;
95 b[i++] = tl >>> 16 & 0xff;
96 b[i++] = tl >>> 8 & 0xff;
97 b[i++] = tl & 0xff;
98
99 // `time_mid`
100 var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
101 b[i++] = tmh >>> 8 & 0xff;
102 b[i++] = tmh & 0xff;
103
104 // `time_high_and_version`
105 b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
106 b[i++] = tmh >>> 16 & 0xff;
107
108 // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
109 b[i++] = clockseq >>> 8 | 0x80;
110
111 // `clock_seq_low`
112 b[i++] = clockseq & 0xff;
113
114 // `node`
115 var node = options.node || _nodeId;
116 for (var n = 0; n < 6; ++n) {
117 b[i + n] = node[n];
118 }
119
120 return buf ? buf : buff_to_string(b);
121}
122
123// **`v4()` - Generate random UUID**
124
125// See https://github.com/broofa/node-uuid for API details
126function v4(options, buf, offset) {
127 // Deprecated - 'format' argument, as supported in v1.2
128 var i = buf && offset || 0;
129
130 if (typeof(options) == 'string') {
131 buf = options == 'binary' ? new Array(16) : null;
132 options = null;
133 }
134 options = options || {};
135
136 var rnds = options.random || (options.rng || _rng)();
137
138 // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
139 rnds[6] = (rnds[6] & 0x0f) | 0x40;
140 rnds[8] = (rnds[8] & 0x3f) | 0x80;
141
142 // Copy bytes to buffer, if provided
143 if (buf) {
144 for (var ii = 0; ii < 16; ++ii) {
145 buf[i + ii] = rnds[ii];
146 }
147 }
148
149 return buf || buff_to_string(rnds);
150}
151
152// Export public API
153var uuid = v4;
154uuid.v1 = v1;
155uuid.v4 = v4;
156
157module.exports = uuid;