v7.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import rng from './rng.js';
  2. import { unsafeStringify } from './stringify.js';
  3. /**
  4. * UUID V7 - Unix Epoch time-based UUID
  5. *
  6. * The IETF has published RFC9562, introducing 3 new UUID versions (6,7,8). This
  7. * implementation of V7 is based on the accepted, though not yet approved,
  8. * revisions.
  9. *
  10. * RFC 9562:https://www.rfc-editor.org/rfc/rfc9562.html Universally Unique
  11. * IDentifiers (UUIDs)
  12. *
  13. * Sample V7 value:
  14. * https://www.rfc-editor.org/rfc/rfc9562.html#name-example-of-a-uuidv7-value
  15. *
  16. * Monotonic Bit Layout: RFC rfc9562.6.2 Method 1, Dedicated Counter Bits ref:
  17. * https://www.rfc-editor.org/rfc/rfc9562.html#section-6.2-5.1
  18. *
  19. * 0 1 2 3 0 1 2 3 4 5 6
  20. * 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
  21. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  22. * | unix_ts_ms |
  23. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  24. * | unix_ts_ms | ver | seq_hi |
  25. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  26. * |var| seq_low | rand |
  27. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  28. * | rand |
  29. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  30. *
  31. * seq is a 31 bit serialized counter; comprised of 12 bit seq_hi and 19 bit
  32. * seq_low, and randomly initialized upon timestamp change. 31 bit counter size
  33. * was selected as any bitwise operations in node are done as _signed_ 32 bit
  34. * ints. we exclude the sign bit.
  35. */
  36. var _seqLow = null;
  37. var _seqHigh = null;
  38. var _msecs = 0;
  39. function v7(options, buf, offset) {
  40. options = options || {};
  41. // initialize buffer and pointer
  42. var i = buf && offset || 0;
  43. var b = buf || new Uint8Array(16);
  44. // rnds is Uint8Array(16) filled with random bytes
  45. var rnds = options.random || (options.rng || rng)();
  46. // milliseconds since unix epoch, 1970-01-01 00:00
  47. var msecs = options.msecs !== undefined ? options.msecs : Date.now();
  48. // seq is user provided 31 bit counter
  49. var seq = options.seq !== undefined ? options.seq : null;
  50. // initialize local seq high/low parts
  51. var seqHigh = _seqHigh;
  52. var seqLow = _seqLow;
  53. // check if clock has advanced and user has not provided msecs
  54. if (msecs > _msecs && options.msecs === undefined) {
  55. _msecs = msecs;
  56. // unless user provided seq, reset seq parts
  57. if (seq !== null) {
  58. seqHigh = null;
  59. seqLow = null;
  60. }
  61. }
  62. // if we have a user provided seq
  63. if (seq !== null) {
  64. // trim provided seq to 31 bits of value, avoiding overflow
  65. if (seq > 0x7fffffff) {
  66. seq = 0x7fffffff;
  67. }
  68. // split provided seq into high/low parts
  69. seqHigh = seq >>> 19 & 0xfff;
  70. seqLow = seq & 0x7ffff;
  71. }
  72. // randomly initialize seq
  73. if (seqHigh === null || seqLow === null) {
  74. seqHigh = rnds[6] & 0x7f;
  75. seqHigh = seqHigh << 8 | rnds[7];
  76. seqLow = rnds[8] & 0x3f; // pad for var
  77. seqLow = seqLow << 8 | rnds[9];
  78. seqLow = seqLow << 5 | rnds[10] >>> 3;
  79. }
  80. // increment seq if within msecs window
  81. if (msecs + 10000 > _msecs && seq === null) {
  82. if (++seqLow > 0x7ffff) {
  83. seqLow = 0;
  84. if (++seqHigh > 0xfff) {
  85. seqHigh = 0;
  86. // increment internal _msecs. this allows us to continue incrementing
  87. // while staying monotonic. Note, once we hit 10k milliseconds beyond system
  88. // clock, we will reset breaking monotonicity (after (2^31)*10000 generations)
  89. _msecs++;
  90. }
  91. }
  92. } else {
  93. // resetting; we have advanced more than
  94. // 10k milliseconds beyond system clock
  95. _msecs = msecs;
  96. }
  97. _seqHigh = seqHigh;
  98. _seqLow = seqLow;
  99. // [bytes 0-5] 48 bits of local timestamp
  100. b[i++] = _msecs / 0x10000000000 & 0xff;
  101. b[i++] = _msecs / 0x100000000 & 0xff;
  102. b[i++] = _msecs / 0x1000000 & 0xff;
  103. b[i++] = _msecs / 0x10000 & 0xff;
  104. b[i++] = _msecs / 0x100 & 0xff;
  105. b[i++] = _msecs & 0xff;
  106. // [byte 6] - set 4 bits of version (7) with first 4 bits seq_hi
  107. b[i++] = seqHigh >>> 4 & 0x0f | 0x70;
  108. // [byte 7] remaining 8 bits of seq_hi
  109. b[i++] = seqHigh & 0xff;
  110. // [byte 8] - variant (2 bits), first 6 bits seq_low
  111. b[i++] = seqLow >>> 13 & 0x3f | 0x80;
  112. // [byte 9] 8 bits seq_low
  113. b[i++] = seqLow >>> 5 & 0xff;
  114. // [byte 10] remaining 5 bits seq_low, 3 bits random
  115. b[i++] = seqLow << 3 & 0xff | rnds[10] & 0x07;
  116. // [bytes 11-15] always random
  117. b[i++] = rnds[11];
  118. b[i++] = rnds[12];
  119. b[i++] = rnds[13];
  120. b[i++] = rnds[14];
  121. b[i++] = rnds[15];
  122. return buf || unsafeStringify(b);
  123. }
  124. export default v7;