UNPKG

7.54 kBJavaScriptView Raw
1/*
2 * Generate RFC4122 (v1 and v4) UUIDs
3 *
4 * Documentation at https://github.com/broofa/node-uuid
5 */
6(function() {
7 // Use WHATWG crypto api if available, otherwise shim it
8 // http://wiki.whatwg.org/wiki/Crypto
9 //
10 // (Create a static _rnds array here as well, which lets us avoid per-uuid
11 // array creation)
12 var crypto, _rnds;
13 if (crypto && crypto.getRandomValues) {
14 _rnds = new Uint32Array(4);
15 } else {
16 _rnds = new Array(4);
17 // Math.random does not have the cryptographic-quality guarantee for
18 // randomness that crypto.getRandomValues has, but it's the best we have.
19 crypto = {
20 getRandomValues: function(arr) {
21 for (var i = 0, l = arr.length; i < l; i++) {
22 _rnds[i] = Math.random() * 0x100000000;
23 }
24 }
25 };
26 }
27
28 // Use node.js Buffer class if available, otherwise use the Array class
29 var BufferClass = typeof(Buffer) == 'function' ? Buffer : Array;
30
31 // Maps for number <-> hex string conversion
32 var _octetToHex = [];
33 var _hexToOctet = {};
34 for (var i = 0; i < 256; i++) {
35 _octetToHex[i] = (i + 0x100).toString(16).substr(1);
36 _hexToOctet[_octetToHex[i]] = i;
37 }
38
39 /**
40 * Parse a uuid string into it's component octets.
41 *
42 * This is a loose parser. It parses the first 16 octet pairs as hex
43 * values. If fewer than 16 are found, any remaining entries in the array
44 * are set to zero.
45 *
46 * @param s (String) string to parse.
47 * @param buf (Array|Buffer) Optional buffer to capture parsed values in
48 * @param offset (Number) Optional starting offset into buf
49 */
50 function parse(s, buf, offset) {
51 var buf = buf || new BufferClass(16),
52 i = offset || 0,
53 ii = 0;
54 s.toLowerCase().replace(/[0-9a-f]{2}/g, function(octet) {
55 if (ii < 16) { // Don't overflow!
56 buf[i + ii++] = _hexToOctet[octet];
57 }
58 });
59
60 // Zero out remaining octets if string was short
61 while (ii < 16) {
62 buf[i + ii] = 0;
63 }
64
65 return buf;
66 }
67
68 /**
69 * Generate a uuid string from octet array
70 *
71 * @param buf (Array|Buffer) Optional buffer to pull octets from
72 * @param offset (Number) Optional starting offset into buf
73 */
74 function unparse(buf, offset) {
75 var oth = _octetToHex,
76 b = buf,
77 i = offset || 0;
78 return oth[b[i + 0]] + oth[b[i + 1]] +
79 oth[b[i + 2]] + oth[b[i + 3]] + '-' +
80 oth[b[i + 4]] + oth[b[i + 5]] + '-' +
81 oth[b[i + 6]] + oth[b[i + 7]] + '-' +
82 oth[b[i + 8]] + oth[b[i + 9]] + '-' +
83 oth[b[i + 10]] + oth[b[i + 11]] +
84 oth[b[i + 12]] + oth[b[i + 13]] +
85 oth[b[i + 14]] + oth[b[i + 15]];
86 }
87
88 /**
89 * Create and return octets for a 48-bit node id:
90 * 47 bits random, 1 bit (multicast) set to 1
91 */
92 function _randomNodeId() {
93 crypto.getRandomValues(_rnds);
94
95 return [
96 _rnds[0] & 0xff | 0x01, // Set multicast bit, per 4.1.6 and 4.5
97 _rnds[0] >>> 8 & 0xff,
98 _rnds[0] >>> 16 & 0xff,
99 _rnds[0] >>> 24 & 0xff,
100 _rnds[1] & 0xff,
101 _rnds[1] >>> 8 & 0xff
102 ];
103 }
104
105 // Pre-allocate arrays to avoid per-uuid array creation
106 var _buf = new BufferClass(16);
107
108 //
109 // v1 UUID support
110 //
111 // Inspired by https://github.com/LiosK/UUID.js
112 // and http://docs.python.org/library/uuid.html
113 //
114
115 // Per 4.1.4 - Offset (in msecs) from JS time to UUID (gregorian) time
116 var EPOCH_OFFSET = 12219292800000;
117
118 // Per 4.1.4 - UUID time has 100ns resolution
119 // Per 4.2.1.2 - Count of uuids may be used with low resolution clocks
120 var UUIDS_PER_TICK = 10000;
121
122 // Per 4.5, use a random node id
123 var _nodeId = _randomNodeId();
124
125 // Per 4.2.2, use 14 bit random unsigned integer to initialize clock_seq
126 var _clockSeq = _rnds[2] & 0x3fff;
127
128 // Time of previous uuid creation
129 var _last = 0;
130
131 // # of UUIDs that have been created during current millisecond time tick
132 var _count = 0;
133
134 /**
135 * See docs at https://github.com/broofa/node-uuid
136 */
137 function v1(options, buf, offset) {
138 options = typeof(options) == 'string' ? {format: options} : options || {};
139
140 var b = options.format != 'binary' ? _buf :
141 (buf ? buf : new BufferClass(16));
142 var i = buf && offset || 0;
143
144 // JS Numbers don't have sufficient resolution for keeping time in
145 // 100-nanosecond units, as spec'ed by the RFC, so we kind of kludge things
146 // here by tracking time in JS-msec units, with a second var for the
147 // additional 100-nanosecond units to add to the millisecond-based time.
148 var msecs = 0; // JS time (msecs since Unix epoch)
149 var nsecs = 0; // Additional time (100-nanosecond units), added to msecs
150
151 // Get msecs & nsecs
152 if (options.msecs == null) {
153 // Per 4.2.1.2, use uuid count to simulate higher resolution clock
154 // Get current time and simulate higher clock resolution
155 msecs = new Date().getTime() + EPOCH_OFFSET;
156 _count = (msecs == _last) ? _count + 1 : 0;
157
158 // Per 4.2.1.2 If generator overruns, throw an error
159 // (*Highly* unlikely - requires generating > 10M uuids/sec)
160 if (_count == UUIDS_PER_TICK) {
161 throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
162 }
163
164 // Per 4.2.1.2, if time regresses bump the clock sequence.
165 if (msecs < _last) {
166 _clockSeq++;
167 _count = 0;
168 }
169
170 _last = msecs;
171 nsecs = _count;
172 } else {
173 msecs = options.msecs + EPOCH_OFFSET;
174 nsecs = options.nsecs || 0;
175 }
176 // Per 4.1.4, timestamp composition
177 // time is uuid epoch time in _msecs_
178 var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
179 var tmh = ((msecs / 0x100000000) * 10000) & 0xfffffff;
180 var tm = tmh & 0xffff, th = tmh >> 16;
181 var thav = (th & 0xfff) | 0x1000; // Set version, per 4.1.3
182
183 // Clock sequence
184 var cs = options.clockseq != null ? options.clockseq : _clockSeq;
185
186 // time_low
187 b[i++] = tl >>> 24 & 0xff;
188 b[i++] = tl >>> 16 & 0xff;
189 b[i++] = tl >>> 8 & 0xff;
190 b[i++] = tl & 0xff;
191
192 // time_mid
193 b[i++] = tm >>> 8 & 0xff;
194 b[i++] = tm & 0xff;
195
196 // time_high_and_version
197 b[i++] = thav >>> 8 & 0xff;
198 b[i++] = thav & 0xff;
199
200 // clock_seq_hi_and_reserved (include variant, per 4.2.2)
201 b[i++] = (cs >>> 8) | 0x80;
202
203 // clock_seq_low
204 b[i++] = cs & 0xff;
205
206 // node
207 var node = options.node || _nodeId;
208 for (var n = 0; n < 6; n++) {
209 b[i + n] = node[n];
210 }
211
212 return options.format == null ? unparse(b) : b;
213 }
214
215 //
216 // v4 UUID support
217 //
218
219 /**
220 * See docs at https://github.com/broofa/node-uuid
221 */
222 function v4(options, buf, offset) {
223 options = typeof(options) == 'string' ? {format: options} : options || {};
224
225 var b = options.format != 'binary' ? _buf :
226 (buf ? buf : new BufferClass(16));
227 var i = buf && offset || 0;
228
229 var rnds = options.random || crypto.getRandomValues(_rnds);
230
231 // v4 ideas are all random bits
232 for (var c = 0 ; c < 16; c++) {
233 var ri = c >> 2,
234 rb = (c & 0x03) * 8;
235 b[i + c] = _rnds[ri] >>> rb & 0xff;
236 }
237
238 // ... except for this
239 b[i + 6] = (b[i + 6] & 0x0f) | 0x40; // Per RFC4122 sect. 4.1.3
240 b[i + 8] = (b[i + 8] & 0x3f) | 0x80; // Per RFC4122 sect. 4.4
241
242 return options.format == null ? unparse(b) : b;
243 }
244
245 var uuid = v4;
246 uuid.v1 = v1;
247 uuid.v4 = v4;
248 uuid.parse = parse;
249 uuid.unparse = unparse;
250 uuid.BufferClass = BufferClass;
251
252 if (typeof(module) != 'undefined') {
253 module.exports = uuid;
254 } else {
255 this.uuid = uuid;
256 }
257}());