123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- import rng from './rng.js';
- import { unsafeStringify } from './stringify.js';
- /**
- * UUID V7 - Unix Epoch time-based UUID
- *
- * The IETF has published RFC9562, introducing 3 new UUID versions (6,7,8). This
- * implementation of V7 is based on the accepted, though not yet approved,
- * revisions.
- *
- * RFC 9562:https://www.rfc-editor.org/rfc/rfc9562.html Universally Unique
- * IDentifiers (UUIDs)
- *
- * Sample V7 value:
- * https://www.rfc-editor.org/rfc/rfc9562.html#name-example-of-a-uuidv7-value
- *
- * Monotonic Bit Layout: RFC rfc9562.6.2 Method 1, Dedicated Counter Bits ref:
- * https://www.rfc-editor.org/rfc/rfc9562.html#section-6.2-5.1
- *
- * 0 1 2 3 0 1 2 3 4 5 6
- * 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | unix_ts_ms |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | unix_ts_ms | ver | seq_hi |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * |var| seq_low | rand |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | rand |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *
- * seq is a 31 bit serialized counter; comprised of 12 bit seq_hi and 19 bit
- * seq_low, and randomly initialized upon timestamp change. 31 bit counter size
- * was selected as any bitwise operations in node are done as _signed_ 32 bit
- * ints. we exclude the sign bit.
- */
- var _seqLow = null;
- var _seqHigh = null;
- var _msecs = 0;
- function v7(options, buf, offset) {
- options = options || {};
- // initialize buffer and pointer
- var i = buf && offset || 0;
- var b = buf || new Uint8Array(16);
- // rnds is Uint8Array(16) filled with random bytes
- var rnds = options.random || (options.rng || rng)();
- // milliseconds since unix epoch, 1970-01-01 00:00
- var msecs = options.msecs !== undefined ? options.msecs : Date.now();
- // seq is user provided 31 bit counter
- var seq = options.seq !== undefined ? options.seq : null;
- // initialize local seq high/low parts
- var seqHigh = _seqHigh;
- var seqLow = _seqLow;
- // check if clock has advanced and user has not provided msecs
- if (msecs > _msecs && options.msecs === undefined) {
- _msecs = msecs;
- // unless user provided seq, reset seq parts
- if (seq !== null) {
- seqHigh = null;
- seqLow = null;
- }
- }
- // if we have a user provided seq
- if (seq !== null) {
- // trim provided seq to 31 bits of value, avoiding overflow
- if (seq > 0x7fffffff) {
- seq = 0x7fffffff;
- }
- // split provided seq into high/low parts
- seqHigh = seq >>> 19 & 0xfff;
- seqLow = seq & 0x7ffff;
- }
- // randomly initialize seq
- if (seqHigh === null || seqLow === null) {
- seqHigh = rnds[6] & 0x7f;
- seqHigh = seqHigh << 8 | rnds[7];
- seqLow = rnds[8] & 0x3f; // pad for var
- seqLow = seqLow << 8 | rnds[9];
- seqLow = seqLow << 5 | rnds[10] >>> 3;
- }
- // increment seq if within msecs window
- if (msecs + 10000 > _msecs && 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++;
- }
- }
- } else {
- // resetting; we have advanced more than
- // 10k milliseconds beyond system clock
- _msecs = msecs;
- }
- _seqHigh = seqHigh;
- _seqLow = seqLow;
- // [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;
- // [byte 6] - set 4 bits of version (7) with first 4 bits seq_hi
- b[i++] = seqHigh >>> 4 & 0x0f | 0x70;
- // [byte 7] remaining 8 bits of seq_hi
- b[i++] = seqHigh & 0xff;
- // [byte 8] - variant (2 bits), first 6 bits seq_low
- b[i++] = seqLow >>> 13 & 0x3f | 0x80;
- // [byte 9] 8 bits seq_low
- b[i++] = seqLow >>> 5 & 0xff;
- // [byte 10] remaining 5 bits seq_low, 3 bits random
- b[i++] = seqLow << 3 & 0xff | rnds[10] & 0x07;
- // [bytes 11-15] always random
- b[i++] = rnds[11];
- b[i++] = rnds[12];
- b[i++] = rnds[13];
- b[i++] = rnds[14];
- b[i++] = rnds[15];
- return buf || unsafeStringify(b);
- }
- export default v7;
|