From 1324696bf10e2c6a9a64ac45b850ddc2deb848a6 Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Tue, 2 Jun 2020 09:20:15 -0700 Subject: [PATCH 01/17] fix: improved uuidToBytes --- src/v35.js | 47 +++++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/v35.js b/src/v35.js index dc7775fe..ce43e234 100644 --- a/src/v35.js +++ b/src/v35.js @@ -1,29 +1,40 @@ import bytesToUuid from './bytesToUuid.js'; import validate from './validate.js'; -// Int32 to 4 bytes https://stackoverflow.com/a/12965194/3684944 -function numberToBytes(num, bytes, offset) { - for (let i = 0; i < 4; ++i) { - const byte = num & 0xff; - // Fill the 4 bytes right-to-left. - bytes[offset + 3 - i] = byte; - num = (num - byte) / 256; - } -} - function uuidToBytes(uuid) { if (!validate(uuid)) { return []; } - const bytes = new Array(16); - - numberToBytes(parseInt(uuid.slice(0, 8), 16), bytes, 0); - numberToBytes(parseInt(uuid.slice(9, 13) + uuid.slice(14, 18), 16), bytes, 4); - numberToBytes(parseInt(uuid.slice(19, 23) + uuid.slice(24, 28), 16), bytes, 8); - numberToBytes(parseInt(uuid.slice(28), 16), bytes, 12); - - return bytes; + let v; + return [ + // Parse ########-....-....-....-............ + (v = parseInt(uuid.slice(0, 8), 16)) >> 24, + (v >> 16) & 0xff, + (v >> 8) & 0xff, + v & 0xff, + + // Parse ........-####-....-....-............ + (v = parseInt(uuid.slice(9, 13), 16)) >> 8, + v & 0xff, + + // Parse ........-....-####-....-............ + (v = parseInt(uuid.slice(14, 18), 16)) >> 8, + v & 0xff, + + // Parse ........-....-....-####-............ + (v = parseInt(uuid.slice(19, 23), 16)) >> 8, + v & 0xff, + + // Parse ........-....-....-....-############ + // (This uses "/" operator for high-order bytes to avoid 32-bit truncation) + ((v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000) & 0xff, + (v / 0x100000000) & 0xff, + (v >> 24) & 0xff, + (v >> 16) & 0xff, + (v >> 8) & 0xff, + v & 0xff, + ]; } function stringToBytes(str) { From 1d95f011169930e85fff4e3ae17062e1fb6f2b6c Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Tue, 2 Jun 2020 10:04:25 -0700 Subject: [PATCH 02/17] chore: comment tweak --- src/v35.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v35.js b/src/v35.js index ce43e234..faeb3357 100644 --- a/src/v35.js +++ b/src/v35.js @@ -27,7 +27,7 @@ function uuidToBytes(uuid) { v & 0xff, // Parse ........-....-....-....-############ - // (This uses "/" operator for high-order bytes to avoid 32-bit truncation) + // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes) ((v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000) & 0xff, (v / 0x100000000) & 0xff, (v >> 24) & 0xff, From 98b74e82fbb40609a375083289817e962327ec71 Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Tue, 2 Jun 2020 10:07:28 -0700 Subject: [PATCH 03/17] chore: bump bundlewatch sizes --- bundlewatch.config.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bundlewatch.config.json b/bundlewatch.config.json index 4a3fb794..9d3f2c91 100644 --- a/bundlewatch.config.json +++ b/bundlewatch.config.json @@ -1,13 +1,13 @@ { "files": [ { "path": "./examples/browser-rollup/dist/v1-size.js", "maxSize": "0.8 kB" }, - { "path": "./examples/browser-rollup/dist/v3-size.js", "maxSize": "1.8 kB" }, + { "path": "./examples/browser-rollup/dist/v3-size.js", "maxSize": "1.9 kB" }, { "path": "./examples/browser-rollup/dist/v4-size.js", "maxSize": "0.5 kB" }, - { "path": "./examples/browser-rollup/dist/v5-size.js", "maxSize": "1.2 kB" }, + { "path": "./examples/browser-rollup/dist/v5-size.js", "maxSize": "1.3 kB" }, { "path": "./examples/browser-webpack/dist/v1-size.js", "maxSize": "1.3 kB" }, - { "path": "./examples/browser-webpack/dist/v3-size.js", "maxSize": "2.2 kB" }, + { "path": "./examples/browser-webpack/dist/v3-size.js", "maxSize": "2.4 kB" }, { "path": "./examples/browser-webpack/dist/v4-size.js", "maxSize": "0.9 kB" }, - { "path": "./examples/browser-webpack/dist/v5-size.js", "maxSize": "1.6 kB" } + { "path": "./examples/browser-webpack/dist/v5-size.js", "maxSize": "1.7 kB" } ] } From b85e64b43ee11ba261662b5aa7e01c181f86ec12 Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Thu, 11 Jun 2020 09:09:56 -0700 Subject: [PATCH 04/17] fix: uuidToBytes test --- package-lock.json | 9 +++++++ package.json | 1 + src/uuidToBytes.js | 39 +++++++++++++++++++++++++++++ src/v35.js | 38 +---------------------------- test/unit/uuidToBytes.test.js | 46 +++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 37 deletions(-) create mode 100644 src/uuidToBytes.js create mode 100644 test/unit/uuidToBytes.test.js diff --git a/package-lock.json b/package-lock.json index 0debcb87..0896f995 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12437,6 +12437,15 @@ "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", "dev": true }, + "random-seed": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/random-seed/-/random-seed-0.3.0.tgz", + "integrity": "sha1-2UXy4fOPSejViRNDG4v2u5N1Vs0=", + "dev": true, + "requires": { + "json-stringify-safe": "^5.0.1" + } + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", diff --git a/package.json b/package.json index 6dcccba7..cb150307 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "lint-staged": "10.1.3", "npm-run-all": "4.1.5", "prettier": "2.0.4", + "random-seed": "0.3.0", "rollup": "2.6.1", "rollup-plugin-terser": "5.3.0", "runmd": "1.3.2", diff --git a/src/uuidToBytes.js b/src/uuidToBytes.js new file mode 100644 index 00000000..ba9a4f90 --- /dev/null +++ b/src/uuidToBytes.js @@ -0,0 +1,39 @@ +import validate from './validate.js'; + +function uuidToBytes(uuid) { + if (!validate(uuid)) { + throw TypeError('Invalid UUID'); + } + + let v; + return [ + // Parse ########-....-....-....-............ + (v = parseInt(uuid.slice(0, 8), 16)) >>> 24, + (v >>> 16) & 0xff, + (v >>> 8) & 0xff, + v & 0xff, + + // Parse ........-####-....-....-............ + (v = parseInt(uuid.slice(9, 13), 16)) >>> 8, + v & 0xff, + + // Parse ........-....-####-....-............ + (v = parseInt(uuid.slice(14, 18), 16)) >>> 8, + v & 0xff, + + // Parse ........-....-....-####-............ + (v = parseInt(uuid.slice(19, 23), 16)) >>> 8, + v & 0xff, + + // Parse ........-....-....-....-############ + // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes) + ((v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000) & 0xff, + (v / 0x100000000) & 0xff, + (v >>> 24) & 0xff, + (v >>> 16) & 0xff, + (v >>> 8) & 0xff, + v & 0xff, + ]; +} + +export default uuidToBytes; diff --git a/src/v35.js b/src/v35.js index faeb3357..1b6d1070 100644 --- a/src/v35.js +++ b/src/v35.js @@ -1,41 +1,5 @@ import bytesToUuid from './bytesToUuid.js'; -import validate from './validate.js'; - -function uuidToBytes(uuid) { - if (!validate(uuid)) { - return []; - } - - let v; - return [ - // Parse ########-....-....-....-............ - (v = parseInt(uuid.slice(0, 8), 16)) >> 24, - (v >> 16) & 0xff, - (v >> 8) & 0xff, - v & 0xff, - - // Parse ........-####-....-....-............ - (v = parseInt(uuid.slice(9, 13), 16)) >> 8, - v & 0xff, - - // Parse ........-....-####-....-............ - (v = parseInt(uuid.slice(14, 18), 16)) >> 8, - v & 0xff, - - // Parse ........-....-....-####-............ - (v = parseInt(uuid.slice(19, 23), 16)) >> 8, - v & 0xff, - - // Parse ........-....-....-....-############ - // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes) - ((v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000) & 0xff, - (v / 0x100000000) & 0xff, - (v >> 24) & 0xff, - (v >> 16) & 0xff, - (v >> 8) & 0xff, - v & 0xff, - ]; -} +import uuidToBytes from './uuidToBytes.js'; function stringToBytes(str) { str = unescape(encodeURIComponent(str)); // UTF8 escape diff --git a/test/unit/uuidToBytes.test.js b/test/unit/uuidToBytes.test.js new file mode 100644 index 00000000..ec5aaba4 --- /dev/null +++ b/test/unit/uuidToBytes.test.js @@ -0,0 +1,46 @@ +import assert from 'assert'; +import uuidv4 from '../../src/v4.js'; +import uuidToBytes from '../../src/uuidToBytes.js'; +import bytesToUuid from '../../src/bytesToUuid.js'; +import gen from 'random-seed'; + +// Use deterministic PRNG for reproducable tests +const rand = gen.create('He who wonders discovers that this in itself is wonder.'); +function rng(bytes = []) { + for (let i = 0; i < 16; i++) { + bytes[i] = rand(256); + } + return bytes; +} + +describe('uuidToBytes', () => { + test('string->bytes->string symmetry', () => { + // Verify string->bytes->string symmetry for a selection of uuids + for (let i = 0; i < 1000; i++) { + const uuid = uuidv4({ rng }); + assert.equal(bytesToUuid(uuidToBytes(uuid)), uuid); + } + }); + + test('Ignore case', () => { + // Verify upper/lower case neutrality + assert.deepStrictEqual( + uuidToBytes('0f5abcd1-c194-47f3-905b-2df7263a084b'), + uuidToBytes('0f5abcd1-c194-47f3-905b-2df7263a084b'.toUpperCase()), + ); + }); + + test('Null UUID', () => { + // Verify null Uuid + assert.deepStrictEqual( + uuidToBytes('00000000-0000-0000-0000-000000000000'), + new Array(16).fill(0), + ); + }); + + test('Validates', () => { + assert.throws(() => uuidToBytes()); + assert.throws(() => uuidToBytes('invalid uuid')); + assert.throws(() => uuidToBytes('zyxwvuts-rqpo-nmlk-jihg-fedcba000000')); + }); +}); From 2b95ea2d723cfae13b0ce734d67b3dd62de8adc2 Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Thu, 11 Jun 2020 16:00:20 -0700 Subject: [PATCH 05/17] feat: export uuidToBytes, uuidToBytes benchmark --- examples/benchmark/benchmark.html | 1 + examples/benchmark/benchmark.js | 103 +++++++++++++++++++----------- rollup.config.js | 1 + src/index.js | 1 + 4 files changed, 68 insertions(+), 38 deletions(-) diff --git a/examples/benchmark/benchmark.html b/examples/benchmark/benchmark.html index 54be63f8..97056ce1 100644 --- a/examples/benchmark/benchmark.html +++ b/examples/benchmark/benchmark.html @@ -5,6 +5,7 @@ + diff --git a/examples/benchmark/benchmark.js b/examples/benchmark/benchmark.js index dc3ff19f..3b397cdf 100644 --- a/examples/benchmark/benchmark.js +++ b/examples/benchmark/benchmark.js @@ -5,46 +5,73 @@ const uuidv1 = (typeof window !== 'undefined' && window.uuidv1) || require('uuid const uuidv4 = (typeof window !== 'undefined' && window.uuidv4) || require('uuid').v4; const uuidv3 = (typeof window !== 'undefined' && window.uuidv3) || require('uuid').v3; const uuidv5 = (typeof window !== 'undefined' && window.uuidv5) || require('uuid').v5; +const uuidToBytes = + (typeof window !== 'undefined' && window.uuidToBytes) || require('uuid').uuidToBytes; console.log('Starting. Tests take ~1 minute to run ...'); -const array = new Array(16); +function testUuidToBytes() { + const suite = new Benchmark.Suite({ + onError(event) { + console.error(event.target.error); + }, + }); -const suite = new Benchmark.Suite({ - onError(event) { - console.error(event.target.error); - }, -}); + suite + .add('uuidToBytes()', function () { + uuidToBytes('0f5abcd1-c194-47f3-905b-2df7263a084b'); + }) + .on('cycle', function (event) { + console.log(event.target.toString()); + }) + .on('complete', function () { + console.log('---\n'); + }) + .run(); +} -suite - .add('uuidv1()', function () { - uuidv1(); - }) - .add('uuidv1() fill existing array', function () { - try { - uuidv1(null, array, 0); - } catch (err) { - // The spec (https://tools.ietf.org/html/rfc4122#section-4.2.1.2) defines that only 10M/s v1 - // UUIDs can be generated on a single node. This library throws an error if we hit that limit - // (which can happen on modern hardware and modern Node.js versions). - } - }) - .add('uuidv4()', function () { - uuidv4(); - }) - .add('uuidv4() fill existing array', function () { - uuidv4(null, array, 0); - }) - .add('uuidv3()', function () { - uuidv3('hello.example.com', uuidv3.DNS); - }) - .add('uuidv5()', function () { - uuidv5('hello.example.com', uuidv5.DNS); - }) - .on('cycle', function (event) { - console.log(event.target.toString()); - }) - .on('complete', function () { - console.log('Fastest is ' + this.filter('fastest').map('name')); - }) - .run(); +function testGeneration() { + const array = new Array(16); + + const suite = new Benchmark.Suite({ + onError(event) { + console.error(event.target.error); + }, + }); + + suite + .add('uuidv1()', function () { + uuidv1(); + }) + .add('uuidv1() fill existing array', function () { + try { + uuidv1(null, array, 0); + } catch (err) { + // The spec (https://tools.ietf.org/html/rfc4122#section-4.2.1.2) defines that only 10M/s v1 + // UUIDs can be generated on a single node. This library throws an error if we hit that limit + // (which can happen on modern hardware and modern Node.js versions). + } + }) + .add('uuidv4()', function () { + uuidv4(); + }) + .add('uuidv4() fill existing array', function () { + uuidv4(null, array, 0); + }) + .add('uuidv3()', function () { + uuidv3('hello.example.com', uuidv3.DNS); + }) + .add('uuidv5()', function () { + uuidv5('hello.example.com', uuidv5.DNS); + }) + .on('cycle', function (event) { + console.log(event.target.toString()); + }) + .on('complete', function () { + console.log('Fastest is ' + this.filter('fastest').map('name')); + }) + .run(); +} + +testUuidToBytes(); +testGeneration(); diff --git a/rollup.config.js b/rollup.config.js index 34544dfd..23e26a04 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -20,4 +20,5 @@ export default [ chunk('v3', 'uuidv3'), chunk('v4', 'uuidv4'), chunk('v5', 'uuidv5'), + chunk('uuidToBytes', 'uuidToBytes'), ]; diff --git a/src/index.js b/src/index.js index 7702f92a..8c19f29d 100644 --- a/src/index.js +++ b/src/index.js @@ -5,3 +5,4 @@ export { default as v5 } from './v5.js'; export { default as REGEX } from './regex.js'; export { default as version } from './version.js'; export { default as validate } from './validate.js'; +export { default as uuidToBytes } from './uuidToBytes.js'; From 5bc6a95ba80f38eecf2d248c7ce1ff56bab7b681 Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Mon, 22 Jun 2020 12:07:31 -0700 Subject: [PATCH 06/17] chore: review comments --- test/unit/uuidToBytes.test.js | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/test/unit/uuidToBytes.test.js b/test/unit/uuidToBytes.test.js index ec5aaba4..a7f1bc1d 100644 --- a/test/unit/uuidToBytes.test.js +++ b/test/unit/uuidToBytes.test.js @@ -14,15 +14,35 @@ function rng(bytes = []) { } describe('uuidToBytes', () => { - test('string->bytes->string symmetry', () => { - // Verify string->bytes->string symmetry for a selection of uuids + test('String -> bytes parsing', () => { + assert.deepStrictEqual(uuidToBytes('0f5abcd1-c194-47f3-905b-2df7263a084b'), [ + 0x0f, + 0x5a, + 0xbc, + 0xd1, + 0xc1, + 0x94, + 0x47, + 0xf3, + 0x90, + 0x5b, + 0x2d, + 0xf7, + 0x26, + 0x3a, + 0x08, + 0x4b, + ]); + }); + + test('String -> bytes -> string symmetry for assorted uuids', () => { for (let i = 0; i < 1000; i++) { const uuid = uuidv4({ rng }); assert.equal(bytesToUuid(uuidToBytes(uuid)), uuid); } }); - test('Ignore case', () => { + test('Case neutrality', () => { // Verify upper/lower case neutrality assert.deepStrictEqual( uuidToBytes('0f5abcd1-c194-47f3-905b-2df7263a084b'), @@ -30,15 +50,14 @@ describe('uuidToBytes', () => { ); }); - test('Null UUID', () => { - // Verify null Uuid + test('Null UUID case', () => { assert.deepStrictEqual( uuidToBytes('00000000-0000-0000-0000-000000000000'), new Array(16).fill(0), ); }); - test('Validates', () => { + test('UUID validation', () => { assert.throws(() => uuidToBytes()); assert.throws(() => uuidToBytes('invalid uuid')); assert.throws(() => uuidToBytes('zyxwvuts-rqpo-nmlk-jihg-fedcba000000')); From 1ec9f732d877fa50ee90f9a05da84793784fac97 Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Tue, 23 Jun 2020 11:09:10 -0700 Subject: [PATCH 07/17] feat: export validate, parse, stringify methods --- examples/benchmark/benchmark.html | 2 +- examples/benchmark/benchmark.js | 7 ++-- rollup.config.js | 5 ++- src/bytesToUuid.js | 40 ------------------ src/index.js | 2 +- src/{uuidToBytes.js => parse.js} | 8 ++-- src/stringify.js | 52 +++++++++++++++++++++++ src/v1.js | 4 +- src/v35.js | 24 ++++++----- src/v4.js | 4 +- test/unit/parse.test.js | 68 +++++++++++++++++++++++++++++++ test/unit/uuidToBytes.test.js | 65 ----------------------------- 12 files changed, 151 insertions(+), 130 deletions(-) delete mode 100644 src/bytesToUuid.js rename src/{uuidToBytes.js => parse.js} (92%) create mode 100644 src/stringify.js create mode 100644 test/unit/parse.test.js delete mode 100644 test/unit/uuidToBytes.test.js diff --git a/examples/benchmark/benchmark.html b/examples/benchmark/benchmark.html index 97056ce1..8360b2e3 100644 --- a/examples/benchmark/benchmark.html +++ b/examples/benchmark/benchmark.html @@ -5,7 +5,7 @@ - + diff --git a/examples/benchmark/benchmark.js b/examples/benchmark/benchmark.js index 3b397cdf..78c9cf5f 100644 --- a/examples/benchmark/benchmark.js +++ b/examples/benchmark/benchmark.js @@ -5,8 +5,7 @@ const uuidv1 = (typeof window !== 'undefined' && window.uuidv1) || require('uuid const uuidv4 = (typeof window !== 'undefined' && window.uuidv4) || require('uuid').v4; const uuidv3 = (typeof window !== 'undefined' && window.uuidv3) || require('uuid').v3; const uuidv5 = (typeof window !== 'undefined' && window.uuidv5) || require('uuid').v5; -const uuidToBytes = - (typeof window !== 'undefined' && window.uuidToBytes) || require('uuid').uuidToBytes; +const uuidParse = (typeof window !== 'undefined' && window.uuidParse) || require('uuid').parse; console.log('Starting. Tests take ~1 minute to run ...'); @@ -18,8 +17,8 @@ function testUuidToBytes() { }); suite - .add('uuidToBytes()', function () { - uuidToBytes('0f5abcd1-c194-47f3-905b-2df7263a084b'); + .add('uuidParse()', function () { + uuidParse('0f5abcd1-c194-47f3-905b-2df7263a084b'); }) .on('cycle', function (event) { console.log(event.target.toString()); diff --git a/rollup.config.js b/rollup.config.js index 23e26a04..13970df1 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -20,5 +20,8 @@ export default [ chunk('v3', 'uuidv3'), chunk('v4', 'uuidv4'), chunk('v5', 'uuidv5'), - chunk('uuidToBytes', 'uuidToBytes'), + + chunk('validate', 'uuidValidate'), + chunk('parse', 'uuidParse'), + chunk('stringify', 'uuidStringify'), ]; diff --git a/src/bytesToUuid.js b/src/bytesToUuid.js deleted file mode 100644 index 0f57c69f..00000000 --- a/src/bytesToUuid.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Convert array of 16 byte values to UUID string format of the form: - * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX - */ -const byteToHex = []; - -for (let i = 0; i < 256; ++i) { - byteToHex.push((i + 0x100).toString(16).substr(1)); -} - -function bytesToUuid(buf, offset_) { - const offset = offset_ || 0; - - // Note: Be careful editing this code! It's been tuned for performance - // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 - return ( - byteToHex[buf[offset + 0]] + - byteToHex[buf[offset + 1]] + - byteToHex[buf[offset + 2]] + - byteToHex[buf[offset + 3]] + - '-' + - byteToHex[buf[offset + 4]] + - byteToHex[buf[offset + 5]] + - '-' + - byteToHex[buf[offset + 6]] + - byteToHex[buf[offset + 7]] + - '-' + - byteToHex[buf[offset + 8]] + - byteToHex[buf[offset + 9]] + - '-' + - byteToHex[buf[offset + 10]] + - byteToHex[buf[offset + 11]] + - byteToHex[buf[offset + 12]] + - byteToHex[buf[offset + 13]] + - byteToHex[buf[offset + 14]] + - byteToHex[buf[offset + 15]] - ).toLowerCase(); -} - -export default bytesToUuid; diff --git a/src/index.js b/src/index.js index 8c19f29d..fd56289c 100644 --- a/src/index.js +++ b/src/index.js @@ -5,4 +5,4 @@ export { default as v5 } from './v5.js'; export { default as REGEX } from './regex.js'; export { default as version } from './version.js'; export { default as validate } from './validate.js'; -export { default as uuidToBytes } from './uuidToBytes.js'; +export { default as parse } from './parse.js'; diff --git a/src/uuidToBytes.js b/src/parse.js similarity index 92% rename from src/uuidToBytes.js rename to src/parse.js index ba9a4f90..ab421447 100644 --- a/src/uuidToBytes.js +++ b/src/parse.js @@ -1,12 +1,12 @@ import validate from './validate.js'; -function uuidToBytes(uuid) { +function parse(uuid) { if (!validate(uuid)) { throw TypeError('Invalid UUID'); } let v; - return [ + return Uint8Array.of( // Parse ########-....-....-....-............ (v = parseInt(uuid.slice(0, 8), 16)) >>> 24, (v >>> 16) & 0xff, @@ -33,7 +33,7 @@ function uuidToBytes(uuid) { (v >>> 16) & 0xff, (v >>> 8) & 0xff, v & 0xff, - ]; + ); } -export default uuidToBytes; +export default parse; diff --git a/src/stringify.js b/src/stringify.js new file mode 100644 index 00000000..a5c998f6 --- /dev/null +++ b/src/stringify.js @@ -0,0 +1,52 @@ +/** + * Convert array of 16 byte values to UUID string format of the form: + * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + */ +const byteToHex = []; + +for (let i = 0; i < 256; ++i) { + byteToHex.push(i.toString(16).padStart(2, '0')); +} + +function stringify(arr, offset = 0) { + // Note: Be careful editing this code! It's been tuned for performance + // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 + const uuid = ( + byteToHex[arr[offset + 0]] + + byteToHex[arr[offset + 1]] + + byteToHex[arr[offset + 2]] + + byteToHex[arr[offset + 3]] + + '-' + + byteToHex[arr[offset + 4]] + + byteToHex[arr[offset + 5]] + + '-' + + byteToHex[arr[offset + 6]] + + byteToHex[arr[offset + 7]] + + '-' + + byteToHex[arr[offset + 8]] + + byteToHex[arr[offset + 9]] + + '-' + + byteToHex[arr[offset + 10]] + + byteToHex[arr[offset + 11]] + + byteToHex[arr[offset + 12]] + + byteToHex[arr[offset + 13]] + + byteToHex[arr[offset + 14]] + + byteToHex[arr[offset + 15]] + ).toLowerCase(); + + // Sanity check for valid UUID. This works because if any of + // the input array values don't map to a defined hex octet, the string length + // will get blown out (e.g. "74af23d8-85undefined2c44-...") + // + // This is a somewhat crude check, but avoids having to check each value + // individually. + if (uuid.length !== 36) { + throw new TypeError( + 'Invalid result UUID. Please insure input is array-like, and contains 16 integer values 0-255', + ); + } + + return uuid; +} + +export default stringify; diff --git a/src/v1.js b/src/v1.js index dbf4f5ca..0643675e 100644 --- a/src/v1.js +++ b/src/v1.js @@ -1,5 +1,5 @@ import rng from './rng.js'; -import bytesToUuid from './bytesToUuid.js'; +import stringify from './stringify.js'; // **`v1()` - Generate time-based UUID** // @@ -109,7 +109,7 @@ function v1(options, buf, offset) { b[i + n] = node[n]; } - return buf || bytesToUuid(b); + return buf || stringify(b); } export default v1; diff --git a/src/v35.js b/src/v35.js index 1b6d1070..a2d0b860 100644 --- a/src/v35.js +++ b/src/v35.js @@ -1,5 +1,5 @@ -import bytesToUuid from './bytesToUuid.js'; -import uuidToBytes from './uuidToBytes.js'; +import stringify from './stringify.js'; +import parse from './parse.js'; function stringToBytes(str) { str = unescape(encodeURIComponent(str)); // UTF8 escape @@ -23,19 +23,23 @@ export default function (name, version, hashfunc) { } if (typeof namespace === 'string') { - namespace = uuidToBytes(namespace); + namespace = parse(namespace); } - if (!Array.isArray(value)) { - throw TypeError('value must be an array of bytes'); + if (!value[Symbol.iterator]) { + throw TypeError('Value must be iterable'); } - if (!Array.isArray(namespace) || namespace.length !== 16) { - throw TypeError('namespace must be uuid string or an Array of 16 byte values'); + if (!namespace[Symbol.iterator] || namespace.length !== 16) { + throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)'); } - // Per 4.3 - const bytes = hashfunc(namespace.concat(value)); + // Concatenate namespace and value bytes, Per 4.3 + let bytes = new Uint8Array(namespace.length + value.length); + bytes.set(namespace); + bytes.set(value, namespace.length); + bytes = hashfunc(bytes); + bytes[6] = (bytes[6] & 0x0f) | version; bytes[8] = (bytes[8] & 0x3f) | 0x80; @@ -49,7 +53,7 @@ export default function (name, version, hashfunc) { return buf; } - return bytesToUuid(bytes); + return stringify(bytes); } // Function#name is not settable on some platforms (#270) diff --git a/src/v4.js b/src/v4.js index 16765828..520613a4 100644 --- a/src/v4.js +++ b/src/v4.js @@ -1,5 +1,5 @@ import rng from './rng.js'; -import bytesToUuid from './bytesToUuid.js'; +import stringify from './stringify.js'; function v4(options, buf, offset) { options = options || {}; @@ -21,7 +21,7 @@ function v4(options, buf, offset) { return buf; } - return bytesToUuid(rnds); + return stringify(rnds); } export default v4; diff --git a/test/unit/parse.test.js b/test/unit/parse.test.js new file mode 100644 index 00000000..7137d953 --- /dev/null +++ b/test/unit/parse.test.js @@ -0,0 +1,68 @@ +import assert from 'assert'; +import uuidv4 from '../../src/v4.js'; +import parse from '../../src/parse.js'; +import stringify from '../../src/stringify.js'; +import gen from 'random-seed'; + +// Use deterministic PRNG for reproducable tests +const rand = gen.create('He who wonders discovers that this in itself is wonder.'); +function rng(bytes = []) { + for (let i = 0; i < 16; i++) { + bytes[i] = rand(256); + } + return bytes; +} + +describe('parse', () => { + test('String -> bytes parsing', () => { + assert.deepStrictEqual( + parse('0f5abcd1-c194-47f3-905b-2df7263a084b'), + Uint8Array.from([ + 0x0f, + 0x5a, + 0xbc, + 0xd1, + 0xc1, + 0x94, + 0x47, + 0xf3, + 0x90, + 0x5b, + 0x2d, + 0xf7, + 0x26, + 0x3a, + 0x08, + 0x4b, + ]), + ); + }); + + test('String -> bytes -> string symmetry for assorted uuids', () => { + for (let i = 0; i < 1000; i++) { + const uuid = uuidv4({ rng }); + assert.equal(stringify(parse(uuid)), uuid); + } + }); + + test('Case neutrality', () => { + // Verify upper/lower case neutrality + assert.deepStrictEqual( + parse('0f5abcd1-c194-47f3-905b-2df7263a084b'), + parse('0f5abcd1-c194-47f3-905b-2df7263a084b'.toUpperCase()), + ); + }); + + test('Null UUID case', () => { + assert.deepStrictEqual( + parse('00000000-0000-0000-0000-000000000000'), + Uint8Array.from(new Array(16).fill(0)), + ); + }); + + test('UUID validation', () => { + assert.throws(() => parse()); + assert.throws(() => parse('invalid uuid')); + assert.throws(() => parse('zyxwvuts-rqpo-nmlk-jihg-fedcba000000')); + }); +}); diff --git a/test/unit/uuidToBytes.test.js b/test/unit/uuidToBytes.test.js deleted file mode 100644 index a7f1bc1d..00000000 --- a/test/unit/uuidToBytes.test.js +++ /dev/null @@ -1,65 +0,0 @@ -import assert from 'assert'; -import uuidv4 from '../../src/v4.js'; -import uuidToBytes from '../../src/uuidToBytes.js'; -import bytesToUuid from '../../src/bytesToUuid.js'; -import gen from 'random-seed'; - -// Use deterministic PRNG for reproducable tests -const rand = gen.create('He who wonders discovers that this in itself is wonder.'); -function rng(bytes = []) { - for (let i = 0; i < 16; i++) { - bytes[i] = rand(256); - } - return bytes; -} - -describe('uuidToBytes', () => { - test('String -> bytes parsing', () => { - assert.deepStrictEqual(uuidToBytes('0f5abcd1-c194-47f3-905b-2df7263a084b'), [ - 0x0f, - 0x5a, - 0xbc, - 0xd1, - 0xc1, - 0x94, - 0x47, - 0xf3, - 0x90, - 0x5b, - 0x2d, - 0xf7, - 0x26, - 0x3a, - 0x08, - 0x4b, - ]); - }); - - test('String -> bytes -> string symmetry for assorted uuids', () => { - for (let i = 0; i < 1000; i++) { - const uuid = uuidv4({ rng }); - assert.equal(bytesToUuid(uuidToBytes(uuid)), uuid); - } - }); - - test('Case neutrality', () => { - // Verify upper/lower case neutrality - assert.deepStrictEqual( - uuidToBytes('0f5abcd1-c194-47f3-905b-2df7263a084b'), - uuidToBytes('0f5abcd1-c194-47f3-905b-2df7263a084b'.toUpperCase()), - ); - }); - - test('Null UUID case', () => { - assert.deepStrictEqual( - uuidToBytes('00000000-0000-0000-0000-000000000000'), - new Array(16).fill(0), - ); - }); - - test('UUID validation', () => { - assert.throws(() => uuidToBytes()); - assert.throws(() => uuidToBytes('invalid uuid')); - assert.throws(() => uuidToBytes('zyxwvuts-rqpo-nmlk-jihg-fedcba000000')); - }); -}); From a04e6cd0af79b2146f989814bd8cf76fae180dbf Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Tue, 23 Jun 2020 11:27:40 -0700 Subject: [PATCH 08/17] chore: stringify unit test and benchmark --- examples/benchmark/benchmark.js | 28 +++++++++++++-- src/index.js | 1 + src/parse.js | 58 +++++++++++++++--------------- test/unit/stringify.test.js | 64 +++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 30 deletions(-) create mode 100644 test/unit/stringify.test.js diff --git a/examples/benchmark/benchmark.js b/examples/benchmark/benchmark.js index 78c9cf5f..df78d3f4 100644 --- a/examples/benchmark/benchmark.js +++ b/examples/benchmark/benchmark.js @@ -6,17 +6,41 @@ const uuidv4 = (typeof window !== 'undefined' && window.uuidv4) || require('uuid const uuidv3 = (typeof window !== 'undefined' && window.uuidv3) || require('uuid').v3; const uuidv5 = (typeof window !== 'undefined' && window.uuidv5) || require('uuid').v5; const uuidParse = (typeof window !== 'undefined' && window.uuidParse) || require('uuid').parse; +const uuidStringify = + (typeof window !== 'undefined' && window.uuidStringify) || require('uuid').stringify; console.log('Starting. Tests take ~1 minute to run ...'); -function testUuidToBytes() { +function testParse() { const suite = new Benchmark.Suite({ onError(event) { console.error(event.target.error); }, }); + const BYTES = [ + 0x0f, + 0x5a, + 0xbc, + 0xd1, + 0xc1, + 0x94, + 0x47, + 0xf3, + 0x90, + 0x5b, + 0x2d, + 0xf7, + 0x26, + 0x3a, + 0x08, + 0x4b, + ]; + suite + .add('uuidStringify()', function () { + uuidStringify(BYTES); + }) .add('uuidParse()', function () { uuidParse('0f5abcd1-c194-47f3-905b-2df7263a084b'); }) @@ -72,5 +96,5 @@ function testGeneration() { .run(); } -testUuidToBytes(); +testParse(); testGeneration(); diff --git a/src/index.js b/src/index.js index fd56289c..9586a544 100644 --- a/src/index.js +++ b/src/index.js @@ -5,4 +5,5 @@ export { default as v5 } from './v5.js'; export { default as REGEX } from './regex.js'; export { default as version } from './version.js'; export { default as validate } from './validate.js'; +export { default as stringify } from './stringify.js'; export { default as parse } from './parse.js'; diff --git a/src/parse.js b/src/parse.js index ab421447..85edd846 100644 --- a/src/parse.js +++ b/src/parse.js @@ -6,34 +6,36 @@ function parse(uuid) { } let v; - return Uint8Array.of( - // Parse ########-....-....-....-............ - (v = parseInt(uuid.slice(0, 8), 16)) >>> 24, - (v >>> 16) & 0xff, - (v >>> 8) & 0xff, - v & 0xff, - - // Parse ........-####-....-....-............ - (v = parseInt(uuid.slice(9, 13), 16)) >>> 8, - v & 0xff, - - // Parse ........-....-####-....-............ - (v = parseInt(uuid.slice(14, 18), 16)) >>> 8, - v & 0xff, - - // Parse ........-....-....-####-............ - (v = parseInt(uuid.slice(19, 23), 16)) >>> 8, - v & 0xff, - - // Parse ........-....-....-....-############ - // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes) - ((v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000) & 0xff, - (v / 0x100000000) & 0xff, - (v >>> 24) & 0xff, - (v >>> 16) & 0xff, - (v >>> 8) & 0xff, - v & 0xff, - ); + const arr = new Uint8Array(16); + + // Parse ########-....-....-....-............ + arr[0] = (v = parseInt(uuid.slice(0, 8), 16)) >>> 24; + arr[1] = (v >>> 16) & 0xff; + arr[2] = (v >>> 8) & 0xff; + arr[3] = v & 0xff; + + // Parse ........-####-....-....-............ + arr[4] = (v = parseInt(uuid.slice(9, 13), 16)) >>> 8; + arr[5] = v & 0xff; + + // Parse ........-....-####-....-............ + arr[6] = (v = parseInt(uuid.slice(14, 18), 16)) >>> 8; + arr[7] = v & 0xff; + + // Parse ........-....-....-####-............ + arr[8] = (v = parseInt(uuid.slice(19, 23), 16)) >>> 8; + arr[9] = v & 0xff; + + // Parse ........-....-....-....-############ + // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes) + arr[10] = ((v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000) & 0xff; + arr[11] = (v / 0x100000000) & 0xff; + arr[12] = (v >>> 24) & 0xff; + arr[13] = (v >>> 16) & 0xff; + arr[14] = (v >>> 8) & 0xff; + arr[15] = v & 0xff; + + return arr; } export default parse; diff --git a/test/unit/stringify.test.js b/test/unit/stringify.test.js new file mode 100644 index 00000000..f022e8e5 --- /dev/null +++ b/test/unit/stringify.test.js @@ -0,0 +1,64 @@ +import assert from 'assert'; +import stringify from '../../src/stringify.js'; + +const BYTES = [ + 0x0f, + 0x5a, + 0xbc, + 0xd1, + 0xc1, + 0x94, + 0x47, + 0xf3, + 0x90, + 0x5b, + 0x2d, + 0xf7, + 0x26, + 0x3a, + 0x08, + 0x4b, +]; + +describe('stringify', () => { + test('Stringify Array', () => { + assert.deepStrictEqual(stringify(BYTES), '0f5abcd1-c194-47f3-905b-2df7263a084b'); + }); + + test('Stringify TypedArray', () => { + assert.deepStrictEqual( + stringify(Uint8Array.from(BYTES)), + '0f5abcd1-c194-47f3-905b-2df7263a084b', + ); + assert.deepStrictEqual( + stringify(Int32Array.from(BYTES)), + '0f5abcd1-c194-47f3-905b-2df7263a084b', + ); + }); + + test('Stringify w/ offset', () => { + assert.deepStrictEqual( + stringify([0, 0, 0, ...BYTES], 3), + '0f5abcd1-c194-47f3-905b-2df7263a084b', + ); + }); + + test('Throws on not enough values', () => { + const bytes = [...BYTES]; + bytes.length = 15; + assert.throws(() => stringify(bytes)); + }); + + test('Throws on undefined value', () => { + const bytes = [...BYTES]; + delete bytes[3]; + bytes.length = 15; + assert.throws(() => stringify(bytes)); + }); + + test('Throws on invalid value', () => { + const bytes = [...BYTES]; + bytes[3] = 256; + assert.throws(() => stringify(bytes)); + }); +}); From 2629e4eec522ef613ccc4be9973cab2437de7912 Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Tue, 23 Jun 2020 11:47:01 -0700 Subject: [PATCH 09/17] fix: stringify test in browser --- examples/benchmark/benchmark.html | 1 + src/sha1-browser.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/examples/benchmark/benchmark.html b/examples/benchmark/benchmark.html index 8360b2e3..2f3bf78f 100644 --- a/examples/benchmark/benchmark.html +++ b/examples/benchmark/benchmark.html @@ -6,6 +6,7 @@ + diff --git a/src/sha1-browser.js b/src/sha1-browser.js index 2bfa2cb0..d2362ca0 100644 --- a/src/sha1-browser.js +++ b/src/sha1-browser.js @@ -29,6 +29,9 @@ function sha1(bytes) { for (let i = 0; i < msg.length; ++i) { bytes.push(msg.charCodeAt(i)); } + } else if (!Array.isArray(bytes)) { + // Convert Array-like to Array + bytes = Array.from(bytes); } bytes.push(0x80); From 0f5cb9c02ffdf387acab02253ed73a9da848ddb3 Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Tue, 23 Jun 2020 11:54:57 -0700 Subject: [PATCH 10/17] feat: add uuidVersion to rollup exports --- rollup.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/rollup.config.js b/rollup.config.js index 13970df1..920d599f 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -21,6 +21,7 @@ export default [ chunk('v4', 'uuidv4'), chunk('v5', 'uuidv5'), + chunk('version', 'uuidVersion'), chunk('validate', 'uuidValidate'), chunk('parse', 'uuidParse'), chunk('stringify', 'uuidStringify'), From dcb709f3331a089d18aac342ddffb472b6b91310 Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Tue, 23 Jun 2020 12:10:28 -0700 Subject: [PATCH 11/17] chore: bump bundlewatch limits --- bundlewatch.config.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bundlewatch.config.json b/bundlewatch.config.json index 9d3f2c91..f3d7de16 100644 --- a/bundlewatch.config.json +++ b/bundlewatch.config.json @@ -1,13 +1,13 @@ { "files": [ - { "path": "./examples/browser-rollup/dist/v1-size.js", "maxSize": "0.8 kB" }, - { "path": "./examples/browser-rollup/dist/v3-size.js", "maxSize": "1.9 kB" }, - { "path": "./examples/browser-rollup/dist/v4-size.js", "maxSize": "0.5 kB" }, - { "path": "./examples/browser-rollup/dist/v5-size.js", "maxSize": "1.3 kB" }, + { "path": "./examples/browser-rollup/dist/v1-size.js", "maxSize": "0.9 kB" }, + { "path": "./examples/browser-rollup/dist/v3-size.js", "maxSize": "2.1 kB" }, + { "path": "./examples/browser-rollup/dist/v4-size.js", "maxSize": "0.6 kB" }, + { "path": "./examples/browser-rollup/dist/v5-size.js", "maxSize": "1.5 kB" }, { "path": "./examples/browser-webpack/dist/v1-size.js", "maxSize": "1.3 kB" }, - { "path": "./examples/browser-webpack/dist/v3-size.js", "maxSize": "2.4 kB" }, - { "path": "./examples/browser-webpack/dist/v4-size.js", "maxSize": "0.9 kB" }, - { "path": "./examples/browser-webpack/dist/v5-size.js", "maxSize": "1.7 kB" } + { "path": "./examples/browser-webpack/dist/v3-size.js", "maxSize": "2.5 kB" }, + { "path": "./examples/browser-webpack/dist/v4-size.js", "maxSize": "1.0 kB" }, + { "path": "./examples/browser-webpack/dist/v5-size.js", "maxSize": "1.9 kB" } ] } From 375bf480c949352250f7d88907f1d438c6e0f5d7 Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Tue, 23 Jun 2020 12:12:21 -0700 Subject: [PATCH 12/17] chore: tweak function name --- examples/benchmark/benchmark.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/benchmark/benchmark.js b/examples/benchmark/benchmark.js index df78d3f4..cc36adcb 100644 --- a/examples/benchmark/benchmark.js +++ b/examples/benchmark/benchmark.js @@ -11,7 +11,7 @@ const uuidStringify = console.log('Starting. Tests take ~1 minute to run ...'); -function testParse() { +function testParseAndStringify() { const suite = new Benchmark.Suite({ onError(event) { console.error(event.target.error); @@ -96,5 +96,5 @@ function testGeneration() { .run(); } -testParse(); +testParseAndStringify(); testGeneration(); From d4a8ea5309ebe7b1af9493fef3058b28f7ad059b Mon Sep 17 00:00:00 2001 From: Christoph Tavan Date: Tue, 23 Jun 2020 22:29:51 +0200 Subject: [PATCH 13/17] test: enable browserstack.console setting --- wdio.conf.js | 1 + 1 file changed, 1 insertion(+) diff --git a/wdio.conf.js b/wdio.conf.js index 4be214bf..a7cea646 100644 --- a/wdio.conf.js +++ b/wdio.conf.js @@ -12,6 +12,7 @@ const commonCapabilities = { name: 'browser test', 'browserstack.local': true, 'browserstack.debug': false, + 'browserstack.console': 'errors', resolution: '1024x768', }; From cc09c59f16a5c6a1f981a441e42c070dee75f0ec Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Tue, 23 Jun 2020 13:54:20 -0700 Subject: [PATCH 14/17] fix: revert padStart --- src/stringify.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stringify.js b/src/stringify.js index a5c998f6..bae7a351 100644 --- a/src/stringify.js +++ b/src/stringify.js @@ -5,7 +5,7 @@ const byteToHex = []; for (let i = 0; i < 256; ++i) { - byteToHex.push(i.toString(16).padStart(2, '0')); + byteToHex.push((i + 0x100).toString(16).substr(1)); } function stringify(arr, offset = 0) { From 6813a913d22809551915cc4d12c677b5ab6dc64e Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Tue, 23 Jun 2020 14:33:57 -0700 Subject: [PATCH 15/17] fix: pr review comments --- src/stringify.js | 2 +- src/v35.js | 10 ++++------ test/unit/stringify.test.js | 17 ++++------------- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/stringify.js b/src/stringify.js index bae7a351..17e2b0df 100644 --- a/src/stringify.js +++ b/src/stringify.js @@ -42,7 +42,7 @@ function stringify(arr, offset = 0) { // individually. if (uuid.length !== 36) { throw new TypeError( - 'Invalid result UUID. Please insure input is array-like, and contains 16 integer values 0-255', + 'Invalid result UUID. Please ensure input is array-like, and contains 16 integer values 0-255', ); } diff --git a/src/v35.js b/src/v35.js index a2d0b860..246eeb8a 100644 --- a/src/v35.js +++ b/src/v35.js @@ -26,15 +26,13 @@ export default function (name, version, hashfunc) { namespace = parse(namespace); } - if (!value[Symbol.iterator]) { - throw TypeError('Value must be iterable'); - } - - if (!namespace[Symbol.iterator] || namespace.length !== 16) { + if (namespace.length !== 16) { throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)'); } - // Concatenate namespace and value bytes, Per 4.3 + // Compute hash of namespace and value, Per 4.3 + // Future: Use spread syntax when supported on all platforms, e.g. `bytes = + // hashfunc([...namespace, ... value])` let bytes = new Uint8Array(namespace.length + value.length); bytes.set(namespace); bytes.set(value, namespace.length); diff --git a/test/unit/stringify.test.js b/test/unit/stringify.test.js index f022e8e5..94de77d1 100644 --- a/test/unit/stringify.test.js +++ b/test/unit/stringify.test.js @@ -22,25 +22,16 @@ const BYTES = [ describe('stringify', () => { test('Stringify Array', () => { - assert.deepStrictEqual(stringify(BYTES), '0f5abcd1-c194-47f3-905b-2df7263a084b'); + assert.equal(stringify(BYTES), '0f5abcd1-c194-47f3-905b-2df7263a084b'); }); test('Stringify TypedArray', () => { - assert.deepStrictEqual( - stringify(Uint8Array.from(BYTES)), - '0f5abcd1-c194-47f3-905b-2df7263a084b', - ); - assert.deepStrictEqual( - stringify(Int32Array.from(BYTES)), - '0f5abcd1-c194-47f3-905b-2df7263a084b', - ); + assert.equal(stringify(Uint8Array.from(BYTES)), '0f5abcd1-c194-47f3-905b-2df7263a084b'); + assert.equal(stringify(Int32Array.from(BYTES)), '0f5abcd1-c194-47f3-905b-2df7263a084b'); }); test('Stringify w/ offset', () => { - assert.deepStrictEqual( - stringify([0, 0, 0, ...BYTES], 3), - '0f5abcd1-c194-47f3-905b-2df7263a084b', - ); + assert.equal(stringify([0, 0, 0, ...BYTES], 3), '0f5abcd1-c194-47f3-905b-2df7263a084b'); }); test('Throws on not enough values', () => { From e76568f0fa2617520fe4c9fe56d7dcdda833f767 Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Tue, 23 Jun 2020 16:38:08 -0700 Subject: [PATCH 16/17] fix: no Array.from in IE11 --- src/sha1-browser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sha1-browser.js b/src/sha1-browser.js index d2362ca0..377dc24c 100644 --- a/src/sha1-browser.js +++ b/src/sha1-browser.js @@ -31,7 +31,7 @@ function sha1(bytes) { } } else if (!Array.isArray(bytes)) { // Convert Array-like to Array - bytes = Array.from(bytes); + bytes = Array.prototype.slice.call(bytes); } bytes.push(0x80); From e5bf27b8bb828cedbbe5fc7e9d93995f4ae0f40c Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Wed, 24 Jun 2020 07:40:19 -0700 Subject: [PATCH 17/17] improvement: hardcode namespace length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Linus Unnebäck --- src/v35.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v35.js b/src/v35.js index 246eeb8a..e8706ff0 100644 --- a/src/v35.js +++ b/src/v35.js @@ -33,7 +33,7 @@ export default function (name, version, hashfunc) { // Compute hash of namespace and value, Per 4.3 // Future: Use spread syntax when supported on all platforms, e.g. `bytes = // hashfunc([...namespace, ... value])` - let bytes = new Uint8Array(namespace.length + value.length); + let bytes = new Uint8Array(16 + value.length); bytes.set(namespace); bytes.set(value, namespace.length); bytes = hashfunc(bytes);