Skip to content

Commit 3a033f6

Browse files
authoredDec 2, 2021
feat: optimize uuid.v1 by 1.3x uuid.v4 by 4.3x (430%) (#597)
This skips input validation when stringifying UUIDs that we _know_ are valid.
1 parent aa11485 commit 3a033f6

File tree

5 files changed

+45
-34
lines changed

5 files changed

+45
-34
lines changed
 

‎src/stringify.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ for (let i = 0; i < 256; ++i) {
1010
byteToHex.push((i + 0x100).toString(16).substr(1));
1111
}
1212

13-
function stringify(arr, offset = 0) {
13+
export function unsafeStringify(arr, offset = 0) {
1414
// Note: Be careful editing this code! It's been tuned for performance
1515
// and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434
16-
const uuid = (
16+
return (
1717
byteToHex[arr[offset + 0]] +
1818
byteToHex[arr[offset + 1]] +
1919
byteToHex[arr[offset + 2]] +
@@ -35,7 +35,10 @@ function stringify(arr, offset = 0) {
3535
byteToHex[arr[offset + 14]] +
3636
byteToHex[arr[offset + 15]]
3737
).toLowerCase();
38+
}
3839

40+
function stringify(arr, offset = 0) {
41+
const uuid = unsafeStringify(arr, offset);
3942
// Consistency check for valid UUID. If this throws, it's likely due to one
4043
// of the following:
4144
// - One or more input array values don't map to a hex octet (leading to

‎src/v1.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import rng from './rng.js';
2-
import stringify from './stringify.js';
2+
import { unsafeStringify } from './stringify.js';
33

44
// **`v1()` - Generate time-based UUID**
55
//
@@ -109,7 +109,7 @@ function v1(options, buf, offset) {
109109
b[i + n] = node[n];
110110
}
111111

112-
return buf || stringify(b);
112+
return buf || unsafeStringify(b);
113113
}
114114

115115
export default v1;

‎src/v35.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import stringify from './stringify.js';
1+
import { unsafeStringify } from './stringify.js';
22
import parse from './parse.js';
33

44
function stringToBytes(str) {
@@ -51,7 +51,7 @@ export default function v35(name, version, hashfunc) {
5151
return buf;
5252
}
5353

54-
return stringify(bytes);
54+
return unsafeStringify(bytes);
5555
}
5656

5757
// Function#name is not settable on some platforms (#270)

‎src/v4.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import native from './native.js';
22
import rng from './rng.js';
3-
import stringify from './stringify.js';
3+
import { unsafeStringify } from './stringify.js';
44

55
function v4(options, buf, offset) {
66
if (native.randomUUID && !buf && !options) {
@@ -26,7 +26,7 @@ function v4(options, buf, offset) {
2626
return buf;
2727
}
2828

29-
return stringify(rnds);
29+
return unsafeStringify(rnds);
3030
}
3131

3232
export default v4;

‎test/unit/stringify.test.js

+34-26
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,48 @@
11
import assert from 'assert';
2-
import stringify from '../../src/stringify.js';
2+
import stringify, { unsafeStringify } from '../../src/stringify.js';
33

44
const BYTES = [
55
0x0f, 0x5a, 0xbc, 0xd1, 0xc1, 0x94, 0x47, 0xf3, 0x90, 0x5b, 0x2d, 0xf7, 0x26, 0x3a, 0x08, 0x4b,
66
];
77

8-
describe('stringify', () => {
9-
test('Stringify Array', () => {
10-
assert.equal(stringify(BYTES), '0f5abcd1-c194-47f3-905b-2df7263a084b');
11-
});
8+
describe('unsafeStringify', () => {
9+
describe('default', () => {
10+
test('Stringify Array', () => {
11+
assert.equal(unsafeStringify(BYTES), '0f5abcd1-c194-47f3-905b-2df7263a084b');
12+
});
1213

13-
test('Stringify TypedArray', () => {
14-
assert.equal(stringify(Uint8Array.from(BYTES)), '0f5abcd1-c194-47f3-905b-2df7263a084b');
15-
assert.equal(stringify(Int32Array.from(BYTES)), '0f5abcd1-c194-47f3-905b-2df7263a084b');
16-
});
14+
test('Stringify TypedArray', () => {
15+
assert.equal(unsafeStringify(Uint8Array.from(BYTES)), '0f5abcd1-c194-47f3-905b-2df7263a084b');
16+
assert.equal(unsafeStringify(Int32Array.from(BYTES)), '0f5abcd1-c194-47f3-905b-2df7263a084b');
17+
});
1718

18-
test('Stringify w/ offset', () => {
19-
assert.equal(stringify([0, 0, 0, ...BYTES], 3), '0f5abcd1-c194-47f3-905b-2df7263a084b');
19+
test('Stringify w/ offset', () => {
20+
assert.equal(unsafeStringify([0, 0, 0, ...BYTES], 3), '0f5abcd1-c194-47f3-905b-2df7263a084b');
21+
});
2022
});
2123

22-
test('Throws on not enough values', () => {
23-
const bytes = [...BYTES];
24-
bytes.length = 15;
25-
assert.throws(() => stringify(bytes));
26-
});
24+
describe('safe', () => {
25+
test('Stringify Array', () => {
26+
assert.equal(stringify(BYTES), '0f5abcd1-c194-47f3-905b-2df7263a084b');
27+
});
2728

28-
test('Throws on undefined value', () => {
29-
const bytes = [...BYTES];
30-
delete bytes[3];
31-
bytes.length = 15;
32-
assert.throws(() => stringify(bytes));
33-
});
29+
test('Throws on not enough values', () => {
30+
const bytes = [...BYTES];
31+
bytes.length = 15;
32+
assert.throws(() => stringify(bytes));
33+
});
34+
35+
test('Throws on undefined value', () => {
36+
const bytes = [...BYTES];
37+
delete bytes[3];
38+
bytes.length = 15;
39+
assert.throws(() => stringify(bytes));
40+
});
3441

35-
test('Throws on invalid value', () => {
36-
const bytes = [...BYTES];
37-
bytes[3] = 256;
38-
assert.throws(() => stringify(bytes));
42+
test('Throws on invalid value', () => {
43+
const bytes = [...BYTES];
44+
bytes[3] = 256;
45+
assert.throws(() => stringify(bytes));
46+
});
3947
});
4048
});

0 commit comments

Comments
 (0)
Please sign in to comment.