diff --git a/src/stringify-internal.js b/src/stringify-internal.js new file mode 100644 index 00000000..c479931d --- /dev/null +++ b/src/stringify-internal.js @@ -0,0 +1,312 @@ +/** + * This is the internal `stringify()` function used by the library. + * It isn't exposed in the index because it assumes no offset. + */ + +import validate from './validate.js'; + +/** + * Convert array of 16 byte values to UUID string format of the form: + * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + */ +const byteToHex = [ + '00', + '01', + '02', + '03', + '04', + '05', + '06', + '07', + '08', + '09', + '0a', + '0b', + '0c', + '0d', + '0e', + '0f', + '10', + '11', + '12', + '13', + '14', + '15', + '16', + '17', + '18', + '19', + '1a', + '1b', + '1c', + '1d', + '1e', + '1f', + '20', + '21', + '22', + '23', + '24', + '25', + '26', + '27', + '28', + '29', + '2a', + '2b', + '2c', + '2d', + '2e', + '2f', + '30', + '31', + '32', + '33', + '34', + '35', + '36', + '37', + '38', + '39', + '3a', + '3b', + '3c', + '3d', + '3e', + '3f', + '40', + '41', + '42', + '43', + '44', + '45', + '46', + '47', + '48', + '49', + '4a', + '4b', + '4c', + '4d', + '4e', + '4f', + '50', + '51', + '52', + '53', + '54', + '55', + '56', + '57', + '58', + '59', + '5a', + '5b', + '5c', + '5d', + '5e', + '5f', + '60', + '61', + '62', + '63', + '64', + '65', + '66', + '67', + '68', + '69', + '6a', + '6b', + '6c', + '6d', + '6e', + '6f', + '70', + '71', + '72', + '73', + '74', + '75', + '76', + '77', + '78', + '79', + '7a', + '7b', + '7c', + '7d', + '7e', + '7f', + '80', + '81', + '82', + '83', + '84', + '85', + '86', + '87', + '88', + '89', + '8a', + '8b', + '8c', + '8d', + '8e', + '8f', + '90', + '91', + '92', + '93', + '94', + '95', + '96', + '97', + '98', + '99', + '9a', + '9b', + '9c', + '9d', + '9e', + '9f', + 'a0', + 'a1', + 'a2', + 'a3', + 'a4', + 'a5', + 'a6', + 'a7', + 'a8', + 'a9', + 'aa', + 'ab', + 'ac', + 'ad', + 'ae', + 'af', + 'b0', + 'b1', + 'b2', + 'b3', + 'b4', + 'b5', + 'b6', + 'b7', + 'b8', + 'b9', + 'ba', + 'bb', + 'bc', + 'bd', + 'be', + 'bf', + 'c0', + 'c1', + 'c2', + 'c3', + 'c4', + 'c5', + 'c6', + 'c7', + 'c8', + 'c9', + 'ca', + 'cb', + 'cc', + 'cd', + 'ce', + 'cf', + 'd0', + 'd1', + 'd2', + 'd3', + 'd4', + 'd5', + 'd6', + 'd7', + 'd8', + 'd9', + 'da', + 'db', + 'dc', + 'dd', + 'de', + 'df', + 'e0', + 'e1', + 'e2', + 'e3', + 'e4', + 'e5', + 'e6', + 'e7', + 'e8', + 'e9', + 'ea', + 'eb', + 'ec', + 'ed', + 'ee', + 'ef', + 'f0', + 'f1', + 'f2', + 'f3', + 'f4', + 'f5', + 'f6', + 'f7', + 'f8', + 'f9', + 'fa', + 'fb', + 'fc', + 'fd', + 'fe', + 'ff', +]; + +export function unsafeStringify(arr) { + // 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[arr[0]] + + byteToHex[arr[1]] + + byteToHex[arr[2]] + + byteToHex[arr[3]] + + '-' + + byteToHex[arr[4]] + + byteToHex[arr[5]] + + '-' + + byteToHex[arr[6]] + + byteToHex[arr[7]] + + '-' + + byteToHex[arr[8]] + + byteToHex[arr[9]] + + '-' + + byteToHex[arr[10]] + + byteToHex[arr[11]] + + byteToHex[arr[12]] + + byteToHex[arr[13]] + + byteToHex[arr[14]] + + byteToHex[arr[15]] + ).toLowerCase(); +} + +function stringify(arr) { + const uuid = unsafeStringify(arr); + // Consistency check for valid UUID. If this throws, it's likely due to one + // of the following: + // - One or more input array values don't map to a hex octet (leading to + // "undefined" in the uuid) + // - Invalid input values for the RFC `version` or `variant` fields + if (!validate(uuid)) { + throw TypeError('Stringified UUID is invalid'); + } + + return uuid; +} + +export default stringify; diff --git a/src/stringify.js b/src/stringify.js index 3e66728d..49401e12 100644 --- a/src/stringify.js +++ b/src/stringify.js @@ -4,11 +4,264 @@ import validate from './validate.js'; * 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).slice(1)); -} +const byteToHex = [ + '00', + '01', + '02', + '03', + '04', + '05', + '06', + '07', + '08', + '09', + '0a', + '0b', + '0c', + '0d', + '0e', + '0f', + '10', + '11', + '12', + '13', + '14', + '15', + '16', + '17', + '18', + '19', + '1a', + '1b', + '1c', + '1d', + '1e', + '1f', + '20', + '21', + '22', + '23', + '24', + '25', + '26', + '27', + '28', + '29', + '2a', + '2b', + '2c', + '2d', + '2e', + '2f', + '30', + '31', + '32', + '33', + '34', + '35', + '36', + '37', + '38', + '39', + '3a', + '3b', + '3c', + '3d', + '3e', + '3f', + '40', + '41', + '42', + '43', + '44', + '45', + '46', + '47', + '48', + '49', + '4a', + '4b', + '4c', + '4d', + '4e', + '4f', + '50', + '51', + '52', + '53', + '54', + '55', + '56', + '57', + '58', + '59', + '5a', + '5b', + '5c', + '5d', + '5e', + '5f', + '60', + '61', + '62', + '63', + '64', + '65', + '66', + '67', + '68', + '69', + '6a', + '6b', + '6c', + '6d', + '6e', + '6f', + '70', + '71', + '72', + '73', + '74', + '75', + '76', + '77', + '78', + '79', + '7a', + '7b', + '7c', + '7d', + '7e', + '7f', + '80', + '81', + '82', + '83', + '84', + '85', + '86', + '87', + '88', + '89', + '8a', + '8b', + '8c', + '8d', + '8e', + '8f', + '90', + '91', + '92', + '93', + '94', + '95', + '96', + '97', + '98', + '99', + '9a', + '9b', + '9c', + '9d', + '9e', + '9f', + 'a0', + 'a1', + 'a2', + 'a3', + 'a4', + 'a5', + 'a6', + 'a7', + 'a8', + 'a9', + 'aa', + 'ab', + 'ac', + 'ad', + 'ae', + 'af', + 'b0', + 'b1', + 'b2', + 'b3', + 'b4', + 'b5', + 'b6', + 'b7', + 'b8', + 'b9', + 'ba', + 'bb', + 'bc', + 'bd', + 'be', + 'bf', + 'c0', + 'c1', + 'c2', + 'c3', + 'c4', + 'c5', + 'c6', + 'c7', + 'c8', + 'c9', + 'ca', + 'cb', + 'cc', + 'cd', + 'ce', + 'cf', + 'd0', + 'd1', + 'd2', + 'd3', + 'd4', + 'd5', + 'd6', + 'd7', + 'd8', + 'd9', + 'da', + 'db', + 'dc', + 'dd', + 'de', + 'df', + 'e0', + 'e1', + 'e2', + 'e3', + 'e4', + 'e5', + 'e6', + 'e7', + 'e8', + 'e9', + 'ea', + 'eb', + 'ec', + 'ed', + 'ee', + 'ef', + 'f0', + 'f1', + 'f2', + 'f3', + 'f4', + 'f5', + 'f6', + 'f7', + 'f8', + 'f9', + 'fa', + 'fb', + 'fc', + 'fd', + 'fe', + 'ff', +]; export function unsafeStringify(arr, offset = 0) { // Note: Be careful editing this code! It's been tuned for performance diff --git a/src/v1.js b/src/v1.js index 85b7a7a8..044eb761 100644 --- a/src/v1.js +++ b/src/v1.js @@ -1,5 +1,5 @@ import rng from './rng.js'; -import { unsafeStringify } from './stringify.js'; +import { unsafeStringify } from './stringify-internal.js'; // **`v1()` - Generate time-based UUID** // diff --git a/src/v35.js b/src/v35.js index 1e56eea5..a3cc61dc 100644 --- a/src/v35.js +++ b/src/v35.js @@ -1,4 +1,4 @@ -import { unsafeStringify } from './stringify.js'; +import { unsafeStringify } from './stringify-internal.js'; import parse from './parse.js'; function stringToBytes(str) { diff --git a/src/v4.js b/src/v4.js index dc21c59f..5200d00b 100644 --- a/src/v4.js +++ b/src/v4.js @@ -1,6 +1,6 @@ import native from './native.js'; import rng from './rng.js'; -import { unsafeStringify } from './stringify.js'; +import { unsafeStringify } from './stringify-internal.js'; function v4(options, buf, offset) { if (native.randomUUID && !buf && !options) {