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

fix: msecs option in v7 #768

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 23 additions & 18 deletions src/v7.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ import { unsafeStringify } from './stringify.js';

let _seqLow = null;
let _seqHigh = null;
let _msecs = 0;
let _prevMsecs = 0;
let _overflowMsecs = 0;

function v7(options, buf, offset) {
options = options || {};
Expand All @@ -60,12 +61,13 @@ function v7(options, buf, offset) {
let seqHigh = _seqHigh;
let seqLow = _seqLow;

// check if clock has advanced and user has not provided msecs
if (msecs > _msecs && options.msecs === undefined) {
_msecs = msecs;
// reset if msecs is not the overflow window
if (msecs < _prevMsecs || msecs > _prevMsecs + _overflowMsecs) {
_prevMsecs = msecs;
_overflowMsecs = 0;

// unless user provided seq, reset seq parts
if (seq !== null) {
if (seq === null) {
seqHigh = null;
seqLow = null;
}
Expand Down Expand Up @@ -94,35 +96,38 @@ function v7(options, buf, offset) {
}

// increment seq if within msecs window
if (msecs + 10000 > _msecs && seq === null) {
if (_overflowMsecs < 10000 && seq === null) {
if (++seqLow > 0x7ffff) {
seqLow = 0;

if (++seqHigh > 0xfff) {
seqHigh = 0;

// increment internal _msecs. this allows us to continue incrementing
// while staying monotonic. Note, once we hit 10k milliseconds beyond system
// clock, we will reset breaking monotonicity (after (2^31)*10000 generations)
_msecs++;
// increment internal _overflowMsecs. This allows us to continue incrementing
// while staying monotonic. Note, once we hit 10k milliseconds beyond original
// time, we will reset breaking monotonicity (after (2^31)*10000 generations)
_overflowMsecs++;
}
}
} else {
// resetting; we have advanced more than
// 10k milliseconds beyond system clock
_msecs = msecs;
// 10k milliseconds beyond the original time
_prevMsecs = msecs;
_overflowMsecs = 0;
}

_seqHigh = seqHigh;
_seqLow = seqLow;

const timestamp = _prevMsecs + _overflowMsecs;

// [bytes 0-5] 48 bits of local timestamp
b[i++] = (_msecs / 0x10000000000) & 0xff;
b[i++] = (_msecs / 0x100000000) & 0xff;
b[i++] = (_msecs / 0x1000000) & 0xff;
b[i++] = (_msecs / 0x10000) & 0xff;
b[i++] = (_msecs / 0x100) & 0xff;
b[i++] = _msecs & 0xff;
b[i++] = (timestamp / 0x10000000000) & 0xff;
b[i++] = (timestamp / 0x100000000) & 0xff;
b[i++] = (timestamp / 0x1000000) & 0xff;
b[i++] = (timestamp / 0x10000) & 0xff;
b[i++] = (timestamp / 0x100) & 0xff;
b[i++] = timestamp & 0xff;

// [byte 6] - set 4 bits of version (7) with first 4 bits seq_hi
b[i++] = ((seqHigh >>> 4) & 0x0f) | 0x70;
Expand Down
29 changes: 29 additions & 0 deletions test/unit/v7.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import v7 from '../../src/v7.js';

describe('v7', () => {
const msecsFixture = 1645557742000;
const msecsFutureFixture = Date.UTC('2100');
const seqFixture = 0x661b189b;

const randomBytesFixture = [
Expand Down Expand Up @@ -63,6 +64,26 @@ describe('v7', () => {
assert.strictEqual(id.indexOf('017f22e2'), 0);
});

test('explicit options.msecs after a standard call randomises seq', () => {
const id1 = v7();
const id2 = v7({
msecs: msecsFixture,
});

assert.notDeepStrictEqual(id1.slice(14, 23), id2.slice(14, 23));
});

test('providing current options.msecs after past options.msecs works', () => {
const id1 = v7({
msecs: msecsFixture,
});
const id2 = v7({
msecs: Date.now(),
});

assert.notDeepStrictEqual(id1.slice(0, 13), id2.slice(0, 13));
});

test('fills one UUID into a buffer as expected', () => {
const buffer = [];
const result = v7(
Expand Down Expand Up @@ -169,4 +190,12 @@ describe('v7', () => {

assert(uuid.indexOf('fff') !== 15);
});

test('explicit options.msecs in the future produces expected result', () => {
const id = v7({
msecs: msecsFutureFixture,
});

assert(id.indexOf('03bb2cc3') === 0);
});
});