Skip to content

Commit 1d532ca

Browse files
authoredJul 15, 2024··
fix: seq_hi shift for byte 6 (#775)
1 parent 6dcb15b commit 1d532ca

File tree

2 files changed

+45
-2
lines changed

2 files changed

+45
-2
lines changed
 

‎src/v7.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ function v7(options, buf, offset) {
125125
b[i++] = _msecs & 0xff;
126126

127127
// [byte 6] - set 4 bits of version (7) with first 4 bits seq_hi
128-
b[i++] = ((seqHigh >>> 4) & 0x0f) | 0x70;
128+
b[i++] = ((seqHigh >>> 8) & 0x0f) | 0x70;
129129

130130
// [byte 7] remaining 8 bits of seq_hi
131131
b[i++] = seqHigh & 0xff;

‎test/unit/v7.test.js

+44-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import assert from 'assert';
22
import v7 from '../../src/v7.js';
3+
import stringify from '../../src/stringify.js';
34

45
/**
56
* fixture bit layout:
@@ -154,7 +155,7 @@ describe('v7', () => {
154155
seq,
155156
});
156157

157-
assert.strictEqual(uuid.substr(0, 25), '017f22e2-79b0-7fff-bfff-f');
158+
assert.strictEqual(uuid.substr(0, 25), '017f22e2-79b0-7dff-bfff-f');
158159
});
159160

160161
test('internal seq is reset upon timestamp change', () => {
@@ -169,4 +170,46 @@ describe('v7', () => {
169170

170171
assert(uuid.indexOf('fff') !== 15);
171172
});
173+
174+
test('flipping bits changes the result', () => {
175+
// convert uint8array to BigInt (BE)
176+
const asBigInt = (buf) => buf.reduce((acc, v) => (acc << 8n) | BigInt(v), 0n);
177+
178+
// convert the given number of bits (LE) to number
179+
const asNumber = (bits, data) => Number(BigInt.asUintN(bits, data));
180+
181+
// flip the nth bit (BE) in a BigInt
182+
const flip = (data, n) => data ^ (1n << BigInt(127 - n));
183+
184+
// Extract v7 `options` from a (BigInt) UUID
185+
const optionsFrom = (data) => {
186+
const ms = asNumber(48, data >> (128n - 48n));
187+
const hi = asNumber(12, data >> (43n + 19n + 2n));
188+
const lo = asNumber(19, data >> 43n);
189+
const r = BigInt.asUintN(43, data);
190+
return {
191+
msecs: ms,
192+
seq: (hi << 19) | lo,
193+
random: [
194+
...Array(10).fill(0),
195+
...Array(6)
196+
.fill(0)
197+
.map((_, i) => asNumber(8, r >> (BigInt(i) * 8n)))
198+
.reverse(),
199+
],
200+
};
201+
};
202+
const buf = new Uint8Array(16);
203+
const data = asBigInt(v7({}, buf));
204+
const id = stringify(buf);
205+
const reserved = [48, 49, 50, 51, 64, 65];
206+
for (let i = 0; i < 128; ++i) {
207+
if (reserved.includes(i)) {
208+
continue; // skip bits used for version and variant
209+
}
210+
const flipped = flip(data, i);
211+
assert.strictEqual(asBigInt(v7(optionsFrom(flipped), buf)), flipped, i);
212+
assert.notStrictEqual(stringify(buf), id);
213+
}
214+
});
172215
});

0 commit comments

Comments
 (0)
Please sign in to comment.