Skip to content

Commit d81b9af

Browse files
rangoo94danielleadams
authored andcommitted
crypto: improve randomUUID performance
PR-URL: #37243 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 8e1257e commit d81b9af

File tree

1 file changed

+56
-78
lines changed

1 file changed

+56
-78
lines changed

lib/internal/crypto/random.js

+56-78
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
'use strict';
22

33
const {
4+
Array,
45
BigInt,
56
FunctionPrototypeBind,
67
FunctionPrototypeCall,
78
MathMin,
89
NumberIsNaN,
910
NumberIsSafeInteger,
11+
NumberPrototypeToString,
12+
StringPrototypePadStart,
1013
} = primordials;
1114

1215
const {
@@ -291,104 +294,79 @@ function getRandomValues(data) {
291294
// Implements an RFC 4122 version 4 random UUID.
292295
// To improve performance, random data is generated in batches
293296
// large enough to cover kBatchSize UUID's at a time. The uuidData
294-
// and uuid buffers are reused. Each call to randomUUID() consumes
295-
// 16 bytes from the buffer.
296-
297-
const kHexDigits = [
298-
48, 49, 50, 51, 52, 53, 54, 55,
299-
56, 57, 97, 98, 99, 100, 101, 102,
300-
];
297+
// buffer is reused. Each call to randomUUID() consumes 16 bytes
298+
// from the buffer.
301299

302300
const kBatchSize = 128;
303301
let uuidData;
304302
let uuidNotBuffered;
305-
let uuid;
306303
let uuidBatch = 0;
307304

308-
function getBufferedUUID() {
309-
if (uuidData === undefined) {
310-
uuidData = secureBuffer(16 * kBatchSize);
311-
if (uuidData === undefined)
312-
throw new ERR_OPERATION_FAILED('Out of memory');
305+
let hexBytesCache;
306+
function getHexBytes() {
307+
if (hexBytesCache === undefined) {
308+
hexBytesCache = new Array(256);
309+
for (let i = 0; i < hexBytesCache.length; i++) {
310+
const hex = NumberPrototypeToString(i, 16);
311+
hexBytesCache[i] = StringPrototypePadStart(hex, 2, '0');
312+
}
313313
}
314+
return hexBytesCache;
315+
}
316+
317+
function serializeUUID(buf, offset = 0) {
318+
const kHexBytes = getHexBytes();
319+
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
320+
return kHexBytes[buf[offset]] +
321+
kHexBytes[buf[offset + 1]] +
322+
kHexBytes[buf[offset + 2]] +
323+
kHexBytes[buf[offset + 3]] +
324+
'-' +
325+
kHexBytes[buf[offset + 4]] +
326+
kHexBytes[buf[offset + 5]] +
327+
'-' +
328+
kHexBytes[(buf[offset + 6] & 0x0f) | 0x40] +
329+
kHexBytes[buf[offset + 7]] +
330+
'-' +
331+
kHexBytes[(buf[offset + 8] & 0x3f) | 0x80] +
332+
kHexBytes[buf[offset + 9]] +
333+
'-' +
334+
kHexBytes[buf[offset + 10]] +
335+
kHexBytes[buf[offset + 11]] +
336+
kHexBytes[buf[offset + 12]] +
337+
kHexBytes[buf[offset + 13]] +
338+
kHexBytes[buf[offset + 14]] +
339+
kHexBytes[buf[offset + 15]];
340+
}
341+
342+
function getBufferedUUID() {
343+
uuidData ??= secureBuffer(16 * kBatchSize);
344+
if (uuidData === undefined)
345+
throw new ERR_OPERATION_FAILED('Out of memory');
314346

315347
if (uuidBatch === 0) randomFillSync(uuidData);
316348
uuidBatch = (uuidBatch + 1) % kBatchSize;
317-
return uuidData.slice(uuidBatch * 16, (uuidBatch * 16) + 16);
349+
return serializeUUID(uuidData, uuidBatch * 16);
350+
}
351+
352+
function getUnbufferedUUID() {
353+
uuidNotBuffered ??= secureBuffer(16);
354+
if (uuidNotBuffered === undefined)
355+
throw new ERR_OPERATION_FAILED('Out of memory');
356+
randomFillSync(uuidNotBuffered);
357+
return serializeUUID(uuidNotBuffered);
318358
}
319359

320360
function randomUUID(options) {
321361
if (options !== undefined)
322362
validateObject(options, 'options');
323363
const {
324364
disableEntropyCache = false,
325-
} = { ...options };
365+
} = options || {};
326366

327367
validateBoolean(disableEntropyCache, 'options.disableEntropyCache');
328368

329-
if (uuid === undefined) {
330-
uuid = Buffer.alloc(36, '-');
331-
uuid[14] = 52; // '4', identifies the UUID version
332-
}
333-
334-
let uuidBuf;
335-
if (!disableEntropyCache) {
336-
uuidBuf = getBufferedUUID();
337-
} else {
338-
uuidBuf = uuidNotBuffered;
339-
if (uuidBuf === undefined)
340-
uuidBuf = uuidNotBuffered = secureBuffer(16);
341-
if (uuidBuf === undefined)
342-
throw new ERR_OPERATION_FAILED('Out of memory');
343-
randomFillSync(uuidBuf);
344-
}
345-
346-
// Variant byte: 10xxxxxx (variant 1)
347-
uuidBuf[8] = (uuidBuf[8] & 0x3f) | 0x80;
348-
349-
// This function is structured the way it is for performance.
350-
// The uuid buffer stores the serialization of the random
351-
// bytes from uuidData.
352-
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
353-
let n = 0;
354-
uuid[0] = kHexDigits[uuidBuf[n] >> 4];
355-
uuid[1] = kHexDigits[uuidBuf[n++] & 0xf];
356-
uuid[2] = kHexDigits[uuidBuf[n] >> 4];
357-
uuid[3] = kHexDigits[uuidBuf[n++] & 0xf];
358-
uuid[4] = kHexDigits[uuidBuf[n] >> 4];
359-
uuid[5] = kHexDigits[uuidBuf[n++] & 0xf];
360-
uuid[6] = kHexDigits[uuidBuf[n] >> 4];
361-
uuid[7] = kHexDigits[uuidBuf[n++] & 0xf];
362-
// -
363-
uuid[9] = kHexDigits[uuidBuf[n] >> 4];
364-
uuid[10] = kHexDigits[uuidBuf[n++] & 0xf];
365-
uuid[11] = kHexDigits[uuidBuf[n] >> 4];
366-
uuid[12] = kHexDigits[uuidBuf[n++] & 0xf];
367-
// -
368-
// 4, uuid[14] is set already...
369-
uuid[15] = kHexDigits[uuidBuf[n++] & 0xf];
370-
uuid[16] = kHexDigits[uuidBuf[n] >> 4];
371-
uuid[17] = kHexDigits[uuidBuf[n++] & 0xf];
372-
// -
373-
uuid[19] = kHexDigits[uuidBuf[n] >> 4];
374-
uuid[20] = kHexDigits[uuidBuf[n++] & 0xf];
375-
uuid[21] = kHexDigits[uuidBuf[n] >> 4];
376-
uuid[22] = kHexDigits[uuidBuf[n++] & 0xf];
377-
// -
378-
uuid[24] = kHexDigits[uuidBuf[n] >> 4];
379-
uuid[25] = kHexDigits[uuidBuf[n++] & 0xf];
380-
uuid[26] = kHexDigits[uuidBuf[n] >> 4];
381-
uuid[27] = kHexDigits[uuidBuf[n++] & 0xf];
382-
uuid[28] = kHexDigits[uuidBuf[n] >> 4];
383-
uuid[29] = kHexDigits[uuidBuf[n++] & 0xf];
384-
uuid[30] = kHexDigits[uuidBuf[n] >> 4];
385-
uuid[31] = kHexDigits[uuidBuf[n++] & 0xf];
386-
uuid[32] = kHexDigits[uuidBuf[n] >> 4];
387-
uuid[33] = kHexDigits[uuidBuf[n++] & 0xf];
388-
uuid[34] = kHexDigits[uuidBuf[n] >> 4];
389-
uuid[35] = kHexDigits[uuidBuf[n] & 0xf];
390-
391-
return uuid.latin1Slice(0, 36);
369+
return disableEntropyCache ? getUnbufferedUUID() : getBufferedUUID();
392370
}
393371

394372
function createRandomPrimeJob(type, size, options) {

0 commit comments

Comments
 (0)