-
-
Notifications
You must be signed in to change notification settings - Fork 915
/
Copy pathv7.ts
108 lines (88 loc) · 3.05 KB
/
v7.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import { UUIDTypes, Version7Options } from './_types.js';
import rng from './rng.js';
import { unsafeStringify } from './stringify.js';
type V7State = {
msecs: number;
seq: number;
};
const _state: V7State = {
msecs: -Infinity, // time, milliseconds
seq: 0, // sequence number (32-bits)
};
function v7(options?: Version7Options, buf?: undefined, offset?: number): string;
function v7(options?: Version7Options, buf?: Uint8Array, offset?: number): Uint8Array;
function v7(options?: Version7Options, buf?: Uint8Array, offset?: number): UUIDTypes {
let bytes: Uint8Array;
if (options) {
// With options: Make UUID independent of internal state
bytes = v7Bytes(
options.random ?? options.rng?.() ?? rng(),
options.msecs,
options.seq,
buf,
offset
);
} else {
// No options: Use internal state
const now = Date.now();
const rnds = rng();
updateV7State(_state, now, rnds);
bytes = v7Bytes(rnds, _state.msecs, _state.seq, buf, offset);
}
return buf ? bytes : unsafeStringify(bytes);
}
// (Private!) Do not use. This method is only exported for testing purposes
// and may change without notice.
export function updateV7State(state: V7State, now: number, rnds: Uint8Array) {
if (now > state.msecs) {
// Time has moved on! Pick a new random sequence number
state.seq = (rnds[6] << 23) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9];
state.msecs = now;
} else {
// Bump sequence counter w/ 32-bit rollover
state.seq = (state.seq + 1) | 0;
// 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++;
}
}
return state;
}
function v7Bytes(
rnds: Uint8Array,
msecs: number | undefined,
seq: number | undefined,
buf = new Uint8Array(16),
offset = 0
) {
// Defaults
msecs ??= Date.now();
seq ??= ((rnds[6] * 0x7f) << 24) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9];
// 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` (4 bits) | sequence bits 28-31 (4 bits)
buf[offset++] = 0x70 | ((seq >>> 28) & 0x0f);
// byte 7: sequence bits 20-27 (8 bits)
buf[offset++] = (seq >>> 20) & 0xff;
// byte 8: `variant` (2 bits) | sequence bits 14-19 (6 bits)
buf[offset++] = 0x80 | ((seq >>> 14) & 0x3f);
// byte 9: sequence bits 6-13 (8 bits)
buf[offset++] = (seq >>> 6) & 0xff;
// byte 10: sequence bits 0-5 (6 bits) | random (2 bits)
buf[offset++] = ((seq << 2) & 0xff) | (rnds[10] & 0x03);
// bytes 11-15: random (40 bits)
buf[offset++] = rnds[11];
buf[offset++] = rnds[12];
buf[offset++] = rnds[13];
buf[offset++] = rnds[14];
buf[offset++] = rnds[15];
return buf;
}
export default v7;