Skip to content

Commit

Permalink
fix: seq-related tests
Browse files Browse the repository at this point in the history
  • Loading branch information
broofa committed Jul 16, 2024
1 parent 6e540f3 commit 894b629
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 28 deletions.
27 changes: 14 additions & 13 deletions src/test/v7.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ const RFC_V7 = '017f22e2-79b0-7cc3-98c4-dc0c0c07398f';
const RFC_V7_BYTES = parse('017f22e2-79b0-7cc3-98c4-dc0c0c07398f');
const RFC_MSECS = 0x17f22e279b0;

// `seq` and `random` values needed to create the above RFC uuid. These are
// specific to our implementation here, and are not part of the RFC.
const RFC_SEQ = 0x661b189b;
// `option.seq` for the above RFC uuid
const RFC_SEQ = (0x0cc3 << 20) | (0x98c4dc >> 2);

// `option,random` for the above RFC uuid
const RFC_RANDOM = Uint8Array.of(
0x10,
0x91,
Expand Down Expand Up @@ -197,15 +198,15 @@ describe('v7', () => {
seq,
});

assert.strictEqual(uuid.substr(0, 25), '017f22e2-79b0-7000-891a-2');
assert.strictEqual(uuid.substr(0, 25), '017f22e2-79b0-7000-848d-1');

seq = 0x6fffffff;
uuid = v7({
msecs: RFC_MSECS,
seq,
});

assert.strictEqual(uuid.substr(0, 25), '017f22e2-79b0-7dff-bfff-f');
assert.strictEqual(uuid.substring(0, 25), '017f22e2-79b0-76ff-bfff-f');
});

test('internal seq is reset upon timestamp change', () => {
Expand All @@ -228,18 +229,18 @@ describe('v7', () => {
// convert the given number of bits (LE) to number
const asNumber = (bits: number, data: bigint) => Number(BigInt.asUintN(bits, data));

// flip the nth bit (BE) in a BigInt
// flip the nth bit (BE) in a BigInt
const flip = (data: bigint, n: number) => data ^ (1n << BigInt(127 - n));

// Extract v7 `options` from a (BigInt) UUID
const optionsFrom = (data: bigint): Version7Options => {
const ms = asNumber(48, data >> (128n - 48n));
const hi = asNumber(12, data >> (43n + 19n + 2n));
const lo = asNumber(19, data >> 43n);
const r = BigInt.asUintN(43, data);
const ms = asNumber(48, data >> 80n);
const hi = asNumber(12, data >> 64n);
const lo = asNumber(20, data >> 42n);
const r = BigInt.asUintN(42, data);
return {
msecs: ms,
seq: (hi << 19) | lo,
seq: (hi << 20) | lo,
random: Uint8Array.from([
...Array(10).fill(0),
...Array(6)
Expand All @@ -259,8 +260,8 @@ describe('v7', () => {
}
const flipped = flip(data, i);
assert.strictEqual(
asBigInt(v7(optionsFrom(flipped), buf)),
flipped,
asBigInt(v7(optionsFrom(flipped), buf)).toString(16),
flipped.toString(16),
`Unequal uuids at bit ${i}`
);
assert.notStrictEqual(stringify(buf), id);
Expand Down
30 changes: 15 additions & 15 deletions src/v7.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function v7(options?: Version7Options, buf?: Uint8Array, offset?: number): UUIDT
let bytes: Uint8Array;

if (options) {
// w/ options: Make UUID independent of internal state
// With options: Make UUID independent of internal state
bytes = v7Bytes(
options.random ?? options.rng?.() ?? rng(),
options.msecs,
Expand Down Expand Up @@ -50,8 +50,8 @@ export function updateV7State(state: V7State, now: number, rnds: Uint8Array) {
// Bump sequence counter w/ 32-bit rollover
state.seq = (state.seq + 1) | 0;

// Handle rollover case by bumping timestamp to preserve monotonicity. This
// is allowed by the RFC and will self-correct as the system clock catches
// In case of rollover, bump timestamp to preserve monotonicity. This is
// allowed by the RFC and should self-correct as the system clock catches
// up. See https://www.rfc-editor.org/rfc/rfc9562.html#section-6.2-9.4
if (state.seq === 0) {
state.msecs++;
Expand All @@ -72,30 +72,30 @@ function v7Bytes(
msecs ??= Date.now();
seq ??= ((rnds[6] * 0x7f) << 24) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9];

// [bytes 0-5] 48 bits of local timestamp
// byte 0-5: timestamp (48 bits)
buf[offset++] = (msecs / 0x10000000000) & 0xff;
buf[offset++] = (msecs / 0x100000000) & 0xff;
buf[offset++] = (msecs / 0x1000000) & 0xff;
buf[offset++] = (msecs / 0x10000) & 0xff;
buf[offset++] = (msecs / 0x100) & 0xff;
buf[offset++] = msecs & 0xff;

// [byte 6] - `version` | seq bits 31-28 (4 bits)
buf[offset++] = 0x70 | ((seq >>> 27) & 0x0f);
// byte 6: `version` (4 bits) | sequence bits 28-31 (4 bits)
buf[offset++] = 0x70 | ((seq >>> 28) & 0x0f);

// [byte 7] seq bits 27-20 (8 bits)
buf[offset++] = (seq >>> 19) & 0xff;
// byte 7: sequence bits 20-27 (8 bits)
buf[offset++] = (seq >>> 20) & 0xff;

// [byte 8] - `variant` (2 bits) | seq bits 19-14 (6 bits)
buf[offset++] = ((seq >>> 13) & 0x3f) | 0x80;
// byte 8: `variant` (2 bits) | sequence bits 14-19 (6 bits)
buf[offset++] = 0x80 | ((seq >>> 14) & 0x3f);

// [byte 9] seq bits 13-6 (8 bits)
buf[offset++] = (seq >>> 5) & 0xff;
// byte 9: sequence bits 6-13 (8 bits)
buf[offset++] = (seq >>> 6) & 0xff;

// [byte 10] seq bits 5-0 (6 bits) | random (2 bits)
buf[offset++] = ((seq << 3) & 0xff) | (rnds[10] & 0x07);
// byte 10: sequence bits 0-5 (6 bits) | random (2 bits)
buf[offset++] = ((seq << 2) & 0xff) | (rnds[10] & 0x03);

// [bytes 11-15] always random
// bytes 11-15: random (40 bits)
buf[offset++] = rnds[11];
buf[offset++] = rnds[12];
buf[offset++] = rnds[13];
Expand Down

0 comments on commit 894b629

Please sign in to comment.