Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: parsing non RFC uuid values #455

Merged
merged 13 commits into from
Jun 2, 2020
2 changes: 1 addition & 1 deletion .babelrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module.exports = {
presets: [['@babel/preset-env', { targets: { node: '8' }, modules: 'commonjs' }]],
},
esmBrowser: {
presets: [['@babel/preset-env', { modules: false }]],
presets: [['@babel/preset-env', { targets: { ie: '11' }, modules: false }]],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just checked again and I think this is not necessary:

Sidenote, if no targets are specified, @babel/preset-env will transform all ECMAScript 2015+ code by default.

Copy link
Contributor Author

@awwit awwit May 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ctavan is explicit better than implicit? =)

},
esmNode: {
presets: [['@babel/preset-env', { targets: { node: '8' }, modules: false }]],
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ ddeb27fb-d9a0-4624-be4d-4615062daed4
The default is to generate version 4 UUIDS, however the other versions are supported. Type
`uuid --help` for details:

```
```shell
$ uuid --help

Usage:
Expand Down Expand Up @@ -323,7 +323,7 @@ uuidv4(); // ⇨ '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed'

To run the examples you must first create a dist build of this library in the module root:

```
```shell
npm run build
```

Expand Down
4 changes: 2 additions & 2 deletions README_js.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ ddeb27fb-d9a0-4624-be4d-4615062daed4
The default is to generate version 4 UUIDS, however the other versions are supported. Type
`uuid --help` for details:

```
```shell
$ uuid --help

Usage:
Expand Down Expand Up @@ -312,7 +312,7 @@ uuidv4(); // ⇨ '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed'

To run the examples you must first create a dist build of this library in the module root:

```
```shell
npm run build
```

Expand Down
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ export { default as v1 } from './v1.js';
export { default as v3 } from './v3.js';
export { default as v4 } from './v4.js';
export { default as v5 } from './v5.js';
export { default as getVersionUUID } from './version.js';
export { default as validateUUID } from './validate.js';
export { default as UUID_REGEXP } from './regex.js';
3 changes: 3 additions & 0 deletions src/regex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const UUID_REGEXP = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;

export default UUID_REGEXP;
1 change: 1 addition & 0 deletions src/uuid-bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ switch (version) {
case 'v5': {
const name = args.shift();
let namespace = args.shift();

assert(name != null, 'v5 name not specified');
assert(namespace != null, 'v5 namespace not specified');

Expand Down
34 changes: 21 additions & 13 deletions src/v35.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import bytesToUuid from './bytesToUuid.js';
import validate from './validate.js';

function uuidToBytes(uuid) {
// Note: We assume we're being passed a valid uuid string
const bytes = [];
// Char offset to hex pairs in uuid strings
const HEX_PAIRS = [0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34];

uuid.replace(/[a-fA-F0-9]{2}/g, function (hex) {
bytes.push(parseInt(hex, 16));
});
function uuidToBytes(uuid) {
if (!validate(uuid)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simpler, more compact, more performant(???) implementation:

// Char offset to hex pairs in uuid strings
const HEX_PAIRS=[0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34];

function uuidToBytes(uuid) {
  if (!validate(uuid)) throw TypeError('Invalid UUID');
  return HEX_PAIRS.map(i => parseInt(uuid.slice(i, i + 2), 16));
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@broofa I checked this code performance:

uuidv3() x 198,096 ops/sec ±0.86% (94 runs sampled)
uuidv5() x 200,775 ops/sec ±0.55% (95 runs sampled)

This code is shorter, but less productive.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would again favor conciseness over performance in this case.

throw TypeError('Invalid UUID');
}

return bytes;
return HEX_PAIRS.map((i) => parseInt(uuid.substr(i, 2), 16));
}

function stringToBytes(str) {
Expand All @@ -28,10 +29,13 @@ export const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';

export default function (name, version, hashfunc) {
function generateUUID(value, namespace, buf, offset) {
const off = (buf && offset) || 0;
if (typeof value === 'string') {
value = stringToBytes(value);
}

if (typeof value === 'string') value = stringToBytes(value);
if (typeof namespace === 'string') namespace = uuidToBytes(namespace);
if (typeof namespace === 'string') {
namespace = uuidToBytes(namespace);
}

if (!Array.isArray(value)) {
throw TypeError('value must be an array of bytes');
Expand All @@ -47,12 +51,16 @@ export default function (name, version, hashfunc) {
bytes[8] = (bytes[8] & 0x3f) | 0x80;

if (buf) {
for (let idx = 0; idx < 16; ++idx) {
buf[off + idx] = bytes[idx];
offset = offset || 0;

for (let i = 0; i < 16; ++i) {
buf[offset + i] = bytes[i];
}

return buf;
}

return buf || bytesToUuid(bytes);
return bytesToUuid(bytes);
}

// Function#name is not settable on some platforms (#270)
Expand Down
4 changes: 2 additions & 2 deletions src/v4.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ function v4(options, buf, offset) {

// Copy bytes to buffer, if provided
if (buf) {
const start = offset || 0;
offset = offset || 0;

for (let i = 0; i < 16; ++i) {
buf[start + i] = rnds[i];
buf[offset + i] = rnds[i];
}

return buf;
Expand Down
7 changes: 7 additions & 0 deletions src/validate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import REGEX from './regex.js';

function validate(uuid) {
return typeof uuid === 'string' && REGEX.test(uuid);
}

export default validate;
11 changes: 11 additions & 0 deletions src/version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import validate from './validate.js';

function version(uuid) {
if (!validate(uuid)) {
throw TypeError('Invalid UUID');
}

return parseInt(uuid.substr(14, 1), 16);
}

export default version;
7 changes: 5 additions & 2 deletions test/unit/v35.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import sha1Browser from '../../src/sha1-browser.js';
import v3 from '../../src/v3.js';
import v5 from '../../src/v5.js';

describe('v5', () => {
describe('v35', () => {
const HASH_SAMPLES = [
{
input: '',
Expand All @@ -32,7 +32,10 @@ describe('v5', () => {
];

function hashToHex(hash) {
if (hash instanceof Buffer) hash = Array.from(hash);
if (hash instanceof Buffer) {
hash = Array.from(hash);
}

return hash
.map(function (b) {
return b.toString(16).padStart(2, '0');
Expand Down
31 changes: 31 additions & 0 deletions test/unit/validate.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import assert from 'assert';
import validate from '../../src/validate.js';

describe('validate', () => {
test('validate uuid', () => {
assert.strictEqual(validate('00000000-0000-0000-0000-000000000000'), true);
broofa marked this conversation as resolved.
Show resolved Hide resolved

assert.strictEqual(validate('d9428888-122b-11e1-b85c-61cd3cbb3210'), true);

assert.strictEqual(validate('109156be-c4fb-41ea-b1b4-efe1671c5836'), true);

assert.strictEqual(validate('a981a0c2-68b1-35dc-bcfc-296e52ab01ec'), true);

assert.strictEqual(validate('90123e1c-7512-523e-bb28-76fab9f2f73d'), true);

assert.strictEqual(validate(), false);

assert.strictEqual(validate(''), false);

assert.strictEqual(validate('invalid uuid string'), false);

assert.strictEqual(validate('00000000000000000000000000000000'), false);
broofa marked this conversation as resolved.
Show resolved Hide resolved

assert.strictEqual(
validate(
'=Y00a-f*v00b*-00c-00d#-p00f\b-00g-00h-####00i^^^-00j*1*2*3&-L00k-\n00l-/00m-----00n-fg000-00p-00r+',
),
false,
);
});
});
32 changes: 32 additions & 0 deletions test/unit/version.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import assert from 'assert';
import version from '../../src/version.js';

describe('version', () => {
test('check uuid version', () => {
assert.strictEqual(version('00000000-0000-0000-0000-000000000000'), 0);

assert.strictEqual(version('d9428888-122b-11e1-b85c-61cd3cbb3210'), 1);

assert.strictEqual(version('109156be-c4fb-41ea-b1b4-efe1671c5836'), 4);

assert.strictEqual(version('a981a0c2-68b1-35dc-bcfc-296e52ab01ec'), 3);

assert.strictEqual(version('90123e1c-7512-523e-bb28-76fab9f2f73d'), 5);

assert.throws(() => version());

assert.throws(() => version(''));

assert.throws(() => version('invalid uuid string'));

assert.throws(() => {
version('00000000000000000000000000000000');
});

assert.throws(() => {
version(
'=Y00a-f*v00b*-00c-00d#-p00f\b-00g-00h-####00i^^^-00j*1*2*3&-L00k-\n00l-/00m-----00n-fg000-00p-00r+',
);
});
});
});